diff --git a/DEPS b/DEPS
index 65df147..be695df 100644
--- a/DEPS
+++ b/DEPS
@@ -273,7 +273,7 @@
   'screen_ai_windows_386': 'version:138.01',
 
   # siso CIPD package version.
-  'siso_version': 'git_revision:3753a55d0911b4ea3dd000263088f60edb8d947b',
+  'siso_version': 'git_revision:d9393c2115244b6e4a797189055e4a2b6769a64d',
 
   # download libaom test data
   'download_libaom_testdata': False,
@@ -295,19 +295,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': 'e5f2041ac763622cc191728d4b6db0d67c86e284',
+  'src_internal_revision': '0e9afdc3a302097ee9c1807dc20b8693f48e9ff2',
   # 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': '3ead037d837c32c819d77607e01f208e91caca07',
+  'skia_revision': 'db50a61378fddb9a29efa40e0058cec56de88c1b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'd7fa05296b658db5ecdc143f6cc60185c60001d0',
+  'v8_revision': '770751e8c5989e59409a67716cceb8085f3d23ed',
   # 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': '71b58cdcd7311f9572b064da0a2871e7c313535e',
+  'angle_revision': '70b90f222c491cb9eb678e03f7a3fbf9f99932b5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -319,7 +319,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': 'a934ee9e1fe4397e682f9f18b1f4f061d7400f9d',
+  'boringssl_revision': 'be17ebd7c19cd9f60ff684a953329a25a531a688',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
@@ -331,7 +331,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': 'fa8438ae6b70c57010177de47a9f13d7041a6328',
+  'googletest_revision': '16d4f8eff6d7cefca6975d82a53f8fc995a6feb7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -375,7 +375,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
-  'crossbench_revision': '6b953c0b98c81c00f8458266d07b570f58b40848',
+  'crossbench_revision': 'f32ec85696ba0d27ecf46a56277f53570b562021',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -391,7 +391,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'fa131039ce7473c0f2f6f18c6039e2a9a72b35a4',
+  'devtools_frontend_revision': '79db863aa9b069c683596bd63a754e1ce4f06f90',
   # 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.
@@ -415,7 +415,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': '5c36eda233b37810c8ca6ad00ad0300b0f821890',
+  'dawn_revision': '8a398ff51641a55c6f366211ad42764bd526bad5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -475,7 +475,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': '59e527dbe3326f52bce1bda3dc30e997a68f2443',
+  'nearby_revision': '959322177f40f2e0f1ecacd8a1aea2805e67b62b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -1486,7 +1486,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '51cc9995b6d55b6e3da653848c3dbae2e3d68316',
+    'd56088756d87245fc8f070fc3d7d24a4a09cfc8c',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1716,7 +1716,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/error_prone',
-               'version': 'bd18Z8-1Y85m4C8xYA3z1J_VIhAaE2qRbkrBzn4_200C',
+               'version': '2jzgic7at7ZxQds7qXCsmkfC4bO7d3qnWU5Q7SFUiJwC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1738,7 +1738,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/lint',
-               'version': '7xZFl5M3w3LIJvhKsJYDQoTh5JZ3vr3I_-VWYXiGJC0C',
+               'version': 'PaYB6553MH9GJfamUZLduJESRbN13Clv2N2beHR6IQAC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1749,7 +1749,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/manifest_merger',
-               'version': 'Ym1eWXywKFRvxsgK0yC4bFj9SwZViDqGEPb_w3mAKqMC',
+               'version': '0L0N3_u2ypIxEEtf0k_l36g_2CykW2BNuZBXm-v5qAUC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -2229,7 +2229,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/kotlin_stdlib',
-              'version': '8JF4DFDjaasGcuqxNRqw0DePRizH5yR1-Al191P4K9wC',
+              'version': 'GUpKElqF0PYGB-SP4D5w6p_MuMYQSBrRkGqFGjPhsIYC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -2526,7 +2526,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '188242925b83331305ff6f8f146b2a6667f21ac2',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '410e8a174f36c6293d3aebd3833d04ac3f367b40',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2845,11 +2845,11 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@bb4c9a8250cf96a68bd4c3ba51ef231a59b6c502',
-  'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@963588074b26326ff0426c8953c1235213309bdb',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@4f278f1c86463df2d83dddba19374c4b69384245',
+  'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@5cffd9a796210165631c995f9f58787ea0527eea',
   '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@c9aad99f9276817f18f72a4696239237c83cb775',
-  'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@66fe610946a6d98169f8ebe9ca483f64c4009fa5',
+  'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@736e415ebaa4290d21e42e370db5e933b9dff06d',
   'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@75ad707a587e1469fb53a901b9b68fe9f6fbc11f',
   'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@c913466fdc5004584890f89ff91121bdb2ffd4ba',
   'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@60b640cb931814fcc6dabe4fc61f4738c56579f6',
@@ -2899,7 +2899,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '3d059dd3ca2653d372ea4463dadc53a40baf7fa8',
+    Var('webrtc_git') + '/src.git' + '@' + '4930228a3eaca3411d54b315ed29e5a504ed61da',
 
   # 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.
@@ -3103,7 +3103,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_deps/autorolled',
-              'version': 'gbOkrsK3-7xG1DV1l2XCjQfdKOR_9eY9G2JQ08TEQkwC',
+              'version': '_jfsXVgk5GB8GWdnHRUcV__xkPxOGvUXpabqaMcVBfcC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -4435,7 +4435,7 @@
 
   'src/chrome/browser/glic/e2e_test/internal': {
       'url': Var('chrome_git') + '/chrome/browser/glic/test/internal.git' + '@' +
-        '51bbed30547c704692fb274c1a90099dc5001c7c',
+        '851d0f7cbbe4e97a8edc5b139756b305f509e43f',
       'condition': 'checkout_glic_e2e_tests',
   },
 
@@ -4619,7 +4619,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '315d910d17d14d2132ec008d5867390276e54386',
+        'e3a04a219778f8c35eef841cab715b5afd032e2f',
       'condition': 'checkout_src_internal',
   },
 
@@ -4685,7 +4685,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '5f6cd5274af6da369855f92f45b3721d4707b7e4',
+        'f5eedda01c1ec38b4693396a33e154b66b958def',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index c8e9897..a5603fa 100644
--- a/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -7011,7 +7011,6 @@
     getter referrer
     getter referrerPolicy
     getter signal
-    getter targetAddressSpace
     getter url
     method arrayBuffer
     method blob
diff --git a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
index 36b12d2a..0a227c1 100644
--- a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
@@ -7708,7 +7708,6 @@
     getter referrer
     getter referrerPolicy
     getter signal
-    getter targetAddressSpace
     getter url
     method arrayBuffer
     method blob
diff --git a/ash/accelerometer/accel_gyro_samples_observer.cc b/ash/accelerometer/accel_gyro_samples_observer.cc
index f0dc608..d78981d 100644
--- a/ash/accelerometer/accel_gyro_samples_observer.cc
+++ b/ash/accelerometer/accel_gyro_samples_observer.cc
@@ -2,14 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "ash/accelerometer/accel_gyro_samples_observer.h"
 
 #include <utility>
+#include <vector>
 
 #include "ash/accelerometer/accelerometer_constants.h"
 #include "base/functional/bind.h"
@@ -115,8 +111,8 @@
                  << ": Observer started with no channels enabled";
       if (sensor_device_remote_.is_bound()) {
         sensor_device_remote_->SetChannelsEnabled(
-            std::vector<int32_t>(channel_indices_,
-                                 channel_indices_ + kNumberOfAxes),
+            std::vector<int32_t>(channel_indices_.begin(),
+                                 channel_indices_.end()),
             /*enable=*/true,
             base::BindOnce(
                 &AccelGyroSamplesObserver::SetChannelsEnabledCallback,
@@ -193,7 +189,7 @@
   }
 
   sensor_device_remote_->SetChannelsEnabled(
-      std::vector<int32_t>(channel_indices_, channel_indices_ + kNumberOfAxes),
+      std::vector<int32_t>(channel_indices_.begin(), channel_indices_.end()),
       /*enable=*/true,
       base::BindOnce(&AccelGyroSamplesObserver::SetChannelsEnabledCallback,
                      weak_factory_.GetWeakPtr()));
diff --git a/ash/accelerometer/accel_gyro_samples_observer.h b/ash/accelerometer/accel_gyro_samples_observer.h
index a67f609..73a58ac5 100644
--- a/ash/accelerometer/accel_gyro_samples_observer.h
+++ b/ash/accelerometer/accel_gyro_samples_observer.h
@@ -6,6 +6,8 @@
 #define ASH_ACCELEROMETER_ACCEL_GYRO_SAMPLES_OBSERVER_H_
 
 #include <stdint.h>
+
+#include <array>
 #include <memory>
 #include <string>
 #include <vector>
@@ -93,7 +95,7 @@
   std::vector<std::string> iio_channel_ids_;
   // Channel indices (of accel_x, accel_y, and accel_z respectively) to
   // enable.
-  int32_t channel_indices_[kNumberOfAxes];
+  std::array<int32_t, kNumberOfAxes> channel_indices_;
 
   mojo::Receiver<chromeos::sensors::mojom::SensorDeviceSamplesObserver>
       receiver_{this};
diff --git a/ash/accelerometer/accel_gyro_samples_observer_unittest.cc b/ash/accelerometer/accel_gyro_samples_observer_unittest.cc
index c0ca23d..7e8c932 100644
--- a/ash/accelerometer/accel_gyro_samples_observer_unittest.cc
+++ b/ash/accelerometer/accel_gyro_samples_observer_unittest.cc
@@ -2,13 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "ash/accelerometer/accel_gyro_samples_observer.h"
 
+#include <array>
 #include <memory>
 #include <utility>
 
@@ -28,7 +24,7 @@
 
 constexpr int kFakeGyroscopeId = 2;
 
-constexpr int64_t kFakeSampleData[] = {1, 2, 3};
+constexpr std::array<int64_t, 3> kFakeSampleData = {1, 2, 3};
 
 constexpr double kFakeScaleValue = 10.0;
 
diff --git a/ash/accelerometer/accelerometer_constants.h b/ash/accelerometer/accelerometer_constants.h
index afd1165d..d0a890a3 100644
--- a/ash/accelerometer/accelerometer_constants.h
+++ b/ash/accelerometer/accelerometer_constants.h
@@ -5,13 +5,17 @@
 #ifndef ASH_ACCELEROMETER_ACCELEROMETER_CONSTANTS_H_
 #define ASH_ACCELEROMETER_ACCELEROMETER_CONSTANTS_H_
 
+#include <array>
+
 #include "ash/accelerometer/accelerometer_types.h"
 
 namespace ash {
 
-const char kAccelerometerChannels[][8] = {"accel_x", "accel_y", "accel_z"};
+inline constexpr std::array<const char[8], 3> kAccelerometerChannels = {
+    "accel_x", "accel_y", "accel_z"};
 
-const char kGyroscopeChannels[][10] = {"anglvel_x", "anglvel_y", "anglvel_z"};
+inline constexpr std::array<const char[10], 3> kGyroscopeChannels = {
+    "anglvel_x", "anglvel_y", "anglvel_z"};
 
 // The number of axes for which there are accelerometer readings.
 constexpr uint32_t kNumberOfAxes = 3u;
diff --git a/ash/accelerometer/accelerometer_provider_mojo_unittest.cc b/ash/accelerometer/accelerometer_provider_mojo_unittest.cc
index f2e085c..3f858a5 100644
--- a/ash/accelerometer/accelerometer_provider_mojo_unittest.cc
+++ b/ash/accelerometer/accelerometer_provider_mojo_unittest.cc
@@ -2,21 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/memory/raw_ptr.h"
-
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "ash/accelerometer/accelerometer_provider_mojo.h"
 
+#include <array>
 #include <memory>
 #include <utility>
 
 #include "ash/accelerometer/accelerometer_constants.h"
 #include "ash/accelerometer/accelerometer_reader.h"
 #include "ash/test/ash_test_helper.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
@@ -36,7 +31,7 @@
 constexpr int kFakeBaseAccelerometerId = 2;
 constexpr int kFakeLidAngleId = 3;
 
-constexpr int64_t kFakeSampleData[] = {1, 2, 3};
+constexpr std::array<int64_t, kNumberOfAxes> kFakeSampleData = {1, 2, 3};
 
 class FakeObserver : public AccelerometerReader::Observer {
  public:
diff --git a/ash/accelerometer/accelerometer_types.cc b/ash/accelerometer/accelerometer_types.cc
index 169f5a47..ee2d6943 100644
--- a/ash/accelerometer/accelerometer_types.cc
+++ b/ash/accelerometer/accelerometer_types.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "ash/accelerometer/accelerometer_types.h"
 
 #include <cmath>
diff --git a/ash/accelerometer/accelerometer_types.h b/ash/accelerometer/accelerometer_types.h
index 61fd59ab..8ca8b0b 100644
--- a/ash/accelerometer/accelerometer_types.h
+++ b/ash/accelerometer/accelerometer_types.h
@@ -2,14 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #ifndef ASH_ACCELEROMETER_ACCELEROMETER_TYPES_H_
 #define ASH_ACCELEROMETER_ACCELEROMETER_TYPES_H_
 
+#include <array>
+
 #include "ash/ash_export.h"
 
 namespace gfx {
@@ -80,7 +77,7 @@
   void Reset();
 
  protected:
-  AccelerometerReading data_[ACCELEROMETER_SOURCE_COUNT];
+  std::array<AccelerometerReading, ACCELEROMETER_SOURCE_COUNT> data_;
 };
 
 }  // namespace ash
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 5e9a43a..fdcbc73 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -318,6 +318,11 @@
              "BocaSpotlightRobotRequester",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Enables or disables enforcing sequential execution for Boca insert activity.
+BASE_FEATURE(kBocaSequentialInsertActivity,
+             "BocaSequentialInsertActivity",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 BASE_FEATURE(kCrosSwitcher, "CrosSwitcher", base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Indicates whether the camera super resolution is supported. Note that this
@@ -3554,6 +3559,10 @@
   return base::FeatureList::IsEnabled(kBocaSpotlightRobotRequester);
 }
 
+bool IsBocaSequentialInsertActivityEnabled() {
+  return base::FeatureList::IsEnabled(kBocaSequentialInsertActivity);
+}
+
 bool IsBrightnessControlInSettingsEnabled() {
   return base::FeatureList::IsEnabled(kEnableBrightnessControlInSettings);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index b3d38f9..891e53b 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -111,6 +111,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBocaCaptionToggle);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kBocaSpotlightRobotRequester);
+COMPONENT_EXPORT(ASH_CONSTANTS)
+BASE_DECLARE_FEATURE(kBocaSequentialInsertActivity);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCameraSuperResSupported);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCrosSwitcher);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBorealisBigGl);
@@ -1116,6 +1118,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBocaCaptionToggleEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsBocaSpotlightRobotRequesterEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS)
+bool IsBocaSequentialInsertActivityEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBrightnessControlInSettingsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCaptureModeEducationEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -1134,8 +1138,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCrosSafetyServiceEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCrossDeviceFeatureSuiteAllowed();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCrosSwitcherEnabled();
-COMPONENT_EXPORT(ASH_CONSTANTS)
-bool IsCryptauthAttestationSyncingEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCryptauthAttestationSyncingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDeepLinkingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeAppLandscapeLockedEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeAppResetWindowContainerEnable();
diff --git a/ash/webui/boca_ui/boca_app_page_handler.cc b/ash/webui/boca_ui/boca_app_page_handler.cc
index af0c40a9..524510b 100644
--- a/ash/webui/boca_ui/boca_app_page_handler.cc
+++ b/ash/webui/boca_ui/boca_app_page_handler.cc
@@ -294,6 +294,15 @@
     GetSessionManager()->NotifySessionCaptionProducerEvents(caption_config);
   }
   GetSessionManager()->RemoveObserver(this);
+  if (!is_producer_ || BocaAppClient::Get()->HasApp()) {
+    // Always try end session when handler destructed, but do not proceed if
+    // there is still app instance. Find App won't return the window is already
+    // scheduled to close.
+    return;
+  }
+  // Best effort end session. Not handling response, if update failed,
+  // persistent notification will stay.
+  EndSession(base::BindOnce([](std::optional<mojom::UpdateSessionError>) {}));
 }
 
 void BocaAppHandler::AuthenticateWebview(AuthenticateWebviewCallback callback) {
diff --git a/ash/webui/boca_ui/boca_app_page_handler_unittest.cc b/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
index 6efe264..ea8d7d5 100644
--- a/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
+++ b/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
@@ -452,6 +452,7 @@
   }
 
   void TearDown() override {
+    VerifyEndSession();
     browser_context_ = nullptr;
     boca_app_handler_.reset();
     web_ui_.reset();
@@ -521,6 +522,35 @@
     EXPECT_EQ(get_pref_future.Get(), value);
   }
 
+  void VerifyEndSession() {
+    if (!is_producer_) {
+      return;
+    }
+    EXPECT_CALL(*session_manager(), GetCurrentSession())
+        .WillOnce(Return(&session));
+    EXPECT_CALL(*session_manager(),
+                UpdateCurrentSession(_, /*dispatch_event=*/true))
+        .Times(1);
+
+    // Page handler callback.
+    base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
+                                          google_apis::ApiErrorCode>>
+        future;
+    ::boca::UserIdentity teacher;
+    teacher.set_gaia_id(kGaiaId.ToString());
+    UpdateSessionRequest request(nullptr, kTestUrlBase, teacher,
+                                 session.session_id(), future.GetCallback());
+    EXPECT_CALL(*session_client_impl(), UpdateSession(_))
+        .WillOnce(WithArg<0>(
+            // Unique pointer have ownership issue, have to do manual deep copy
+            // here instead of using SaveArg.
+            Invoke([&](auto request) {
+              ASSERT_EQ(kGaiaId.ToString(), request->teacher().gaia_id());
+              ASSERT_EQ(::boca::Session::PAST, *request->session_state());
+              request->callback().Run(std::make_unique<::boca::Session>());
+            })));
+  }
+
   MockSessionClientImpl* session_client_impl() { return &session_client_impl_; }
   MockBocaAppClient* boca_app_client() { return boca_app_client_.get(); }
   MockSessionManager* session_manager() { return session_manager_.get(); }
diff --git a/ash/webui/boca_ui/boca_ui.cc b/ash/webui/boca_ui/boca_ui.cc
index 5899de1..443c25e 100644
--- a/ash/webui/boca_ui/boca_ui.cc
+++ b/ash/webui/boca_ui/boca_ui.cc
@@ -108,14 +108,7 @@
 #endif  // !DCHECK_IS_ON()
 }
 
-BocaUI::~BocaUI() {
-  if (is_producer_) {
-    // Not handling response, if update failed, persistent notification will
-    // stay.
-    page_handler_impl_->EndSession(
-        base::BindOnce([](std::optional<mojom::UpdateSessionError>) {}));
-  }
-}
+BocaUI::~BocaUI() = default;
 
 void BocaUI::BindInterface(
     mojo::PendingReceiver<boca::mojom::BocaPageHandlerFactory> factory) {
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 8e8cabe..a91110f8 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -7221,8 +7221,7 @@
 // Test reordering desks in RTL mode.
 TEST_P(DesksTest, ReorderDesksInRTLMode) {
   // Turn on RTL mode.
-  const bool default_rtl = base::i18n::IsRTL();
-  base::i18n::SetRTLForTesting(true);
+  base::i18n::ScopedRTLForTesting scoped_rtl(/*rtl=*/true);
   EXPECT_TRUE(base::i18n::IsRTL());
 
   auto* desks_controller = DesksController::Get();
@@ -7308,9 +7307,6 @@
   EXPECT_EQ(1, desks_controller->GetDeskIndex(desk_2));
   EXPECT_EQ(2, desks_controller->GetDeskIndex(desk_0));
   EXPECT_THAT(GetDeskRestoreNames(prefs), ElementsAre("1", "2", "0"));
-
-  // Recover to default RTL mode.
-  base::i18n::SetRTLForTesting(default_rtl);
 }
 
 // Tests the behavior when dragging a desk on the scroll button.
@@ -11284,8 +11280,7 @@
 // desk to the left in the desk bar.
 TEST_P(DeskButtonTest, LayoutInRTL) {
   // Turn on RTL mode.
-  const bool default_rtl = base::i18n::IsRTL();
-  base::i18n::SetRTLForTesting(true);
+  base::i18n::ScopedRTLForTesting scoped_rtl(/*rtl=*/true);
   EXPECT_TRUE(base::i18n::IsRTL());
 
   // The test doesn't start in RTL so we need to tell the widget to swap the
@@ -11363,9 +11358,6 @@
   SwitchToAdjacentDesk(/*next=*/true);
   SwitchToAdjacentDesk(/*next=*/true);
   EXPECT_EQ(2, desks_controller->GetActiveDeskIndex());
-
-  // Recover to default RTL mode.
-  base::i18n::SetRTLForTesting(default_rtl);
 }
 
 TEST_P(DeskButtonTest, BarBoundsWithDeviceSacleFactorChange) {
@@ -11462,17 +11454,13 @@
   UpdateDisplay("800x600");
 
   // Turn on RTL mode.
-  const bool default_rtl = base::i18n::IsRTL();
-  base::i18n::SetRTLForTesting(true);
+  base::i18n::ScopedRTLForTesting scoped_rtl(/*rtl=*/true);
   ASSERT_TRUE(base::i18n::IsRTL());
 
   OpenDeskBar();
   EXPECT_EQ(GetDeskBarView()->bounds(), gfx::Rect(0, 0, 154, 98));
 
   CloseDeskBar();
-
-  // Recover to default RTL mode.
-  base::i18n::SetRTLForTesting(default_rtl);
 }
 
 // Tests that desk button tab order is correct in the shelf.
diff --git a/base/allocator/partition_allocator/src/partition_alloc/gwp_asan_support.cc b/base/allocator/partition_allocator/src/partition_alloc/gwp_asan_support.cc
index d22be1bb..4b4d00a 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/gwp_asan_support.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/gwp_asan_support.cc
@@ -49,8 +49,7 @@
   auto* bucket = root->buckets + bucket_index;
 
   const size_t kSuperPagePayloadStartOffset =
-      internal::SuperPagePayloadStartOffset(
-          /* is_managed_by_normal_buckets = */ true);
+      internal::SuperPagePayloadStartOffset();
   PA_CHECK(kSuperPagePayloadStartOffset % kSlotSize == 0);
   const size_t kSuperPageGwpAsanSlotAreaBeginOffset =
       kSuperPagePayloadStartOffset;
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_address_space.h b/base/allocator/partition_allocator/src/partition_alloc/partition_address_space.h
index bc36744..32d733ce 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_address_space.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_address_space.h
@@ -42,6 +42,12 @@
   PoolOffsetLookup()
       : base_address_(0), base_mask_(static_cast<uintptr_t>(-1)) {}
 
+  PA_ALWAYS_INLINE uintptr_t GetOffset(uintptr_t address) const {
+    PA_DCHECK(Includes(address));
+    return address & ~base_mask_;
+  }
+
+  // Similar to `GetOffset()`, but with MTE tag left in the top bits.
   PA_ALWAYS_INLINE uintptr_t GetTaggedOffset(void* ptr) const {
     const uintptr_t address = reinterpret_cast<uintptr_t>(ptr);
     PA_DCHECK((address & base_mask_) == base_address_);
@@ -53,6 +59,12 @@
     return reinterpret_cast<void*>(base_address_ | tagged_offset);
   }
 
+  // Determines if a given address belongs to address range for this pool.
+  PA_ALWAYS_INLINE bool Includes(uintptr_t address) const {
+    return (UntagAddr(address) & base_mask_) == base_address_;
+  }
+
+  // Ensures that a given offset does not contain a bit for "base" part.
   PA_ALWAYS_INLINE bool IsValidTaggedOffset(uintptr_t tagged_offset) const {
     return !(tagged_offset & base_mask_ & ~kPtrTagMask);
   }
@@ -383,10 +395,11 @@
   void* operator new(size_t) = delete;
   void* operator new(size_t, void*) = delete;
 
- private:
 #if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE)
+ private:
   static bool IsIOSTestProcess();
 
+ public:
   PA_ALWAYS_INLINE static size_t CorePoolSize() {
     return IsIOSTestProcess() ? kCorePoolSizeForIOSTestProcess : kCorePoolSize;
   }
@@ -397,12 +410,19 @@
   }
 #endif  // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE)
 
+  // Almost always equals to `CorePoolSize()`, except on iOS.
+  // Guaranteed to be a compile-time constant.
+  PA_ALWAYS_INLINE static constexpr size_t CorePoolMaxSize() {
+    return kCorePoolSize;
+  }
+
 #if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
   PA_ALWAYS_INLINE static constexpr size_t ThreadIsolatedPoolSize() {
     return kThreadIsolatedPoolSize;
   }
 #endif
 
+ private:
   // On 64-bit systems, PA allocates from several contiguous, mutually disjoint
   // pools. The BRP pool is where all allocations have a BRP ref-count, thus
   // pointers pointing there can use a BRP protection against UaF. Allocations
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc
index 792aee97..13679c7 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc
@@ -804,27 +804,26 @@
 #endif  // PA_BUILDFLAG(IS_APPLE)
 
 bool IsManagedByNormalBucketsForTesting(uintptr_t address,
-                                        [[maybe_unused]] PartitionRoot* root) {
-  return IsManagedByNormalBuckets(address)
+                                        PartitionRoot* root) {
+  return root->GetReservationOffsetTable().IsManagedByNormalBuckets(address)
 #if PA_BUILDFLAG(IS_APPLE)
          && IsNormalBucketsAllocatedByRoot(address, root)
 #endif  // PA_BUILDFLAG(IS_APPLE)
       ;
 }
 
-bool IsManagedByDirectMapForTesting(uintptr_t address,
-                                    [[maybe_unused]] PartitionRoot* root) {
-  return IsManagedByDirectMap(address)
+bool IsManagedByDirectMapForTesting(uintptr_t address, PartitionRoot* root) {
+  return root->GetReservationOffsetTable().IsManagedByDirectMap(address)
 #if PA_BUILDFLAG(IS_APPLE)
          && IsDirectMapAllocatedByRoot(address, root)
 #endif  // PA_BUILDFLAG(IS_APPLE)
       ;
 }
 
-bool IsManagedByNormalBucketsOrDirectMapForTesting(
-    uintptr_t address,
-    [[maybe_unused]] PartitionRoot* root) {
-  return IsManagedByNormalBucketsOrDirectMap(address)
+bool IsManagedByNormalBucketsOrDirectMapForTesting(uintptr_t address,
+                                                   PartitionRoot* root) {
+  return root->GetReservationOffsetTable().IsManagedByNormalBucketsOrDirectMap(
+             address)
 #if PA_BUILDFLAG(IS_APPLE)
          && (IsManagedByNormalBucketsForTesting(address, root) ||
              IsManagedByDirectMapForTesting(address, root))
@@ -5315,11 +5314,19 @@
 #endif  // PA_BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS)
 
 TEST_P(PartitionAllocTest, ReservationOffset) {
+  static constexpr uint16_t kOffsetTagNotAllocated =
+      std::numeric_limits<uint16_t>::max();
+  static constexpr uint16_t kOffsetTagNormalBuckets =
+      std::numeric_limits<uint16_t>::max() - 1;
+
+  ReservationOffsetTable table = allocator.root()->GetReservationOffsetTable();
+
   // For normal buckets, offset should be kOffsetTagNormalBuckets.
   void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name);
   EXPECT_TRUE(ptr);
   uintptr_t address = UntagPtr(ptr);
-  EXPECT_EQ(kOffsetTagNormalBuckets, *ReservationOffsetPointer(address));
+  EXPECT_EQ(kOffsetTagNormalBuckets,
+            *table.GetOffsetPointerForTesting(address));
   allocator.root()->Free(ptr);
 
   // For direct-map,
@@ -5328,36 +5335,44 @@
   ptr = allocator.root()->Alloc(large_size, type_name);
   EXPECT_TRUE(ptr);
   address = UntagPtr(ptr);
-  EXPECT_EQ(0U, *ReservationOffsetPointer(address));
-  EXPECT_EQ(1U, *ReservationOffsetPointer(address + kSuperPageSize));
-  EXPECT_EQ(2U, *ReservationOffsetPointer(address + kSuperPageSize * 2));
-  EXPECT_EQ(3U, *ReservationOffsetPointer(address + kSuperPageSize * 3));
-  EXPECT_EQ(4U, *ReservationOffsetPointer(address + kSuperPageSize * 4));
-  EXPECT_EQ(5U, *ReservationOffsetPointer(address + kSuperPageSize * 5));
+  EXPECT_EQ(0U, *table.GetOffsetPointerForTesting(address));
+  EXPECT_EQ(1U, *table.GetOffsetPointerForTesting(address + kSuperPageSize));
+  EXPECT_EQ(2U,
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 2));
+  EXPECT_EQ(3U,
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 3));
+  EXPECT_EQ(4U,
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 4));
+  EXPECT_EQ(5U,
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 5));
 
   // In-place realloc doesn't affect the offsets.
   void* new_ptr = allocator.root()->Realloc(ptr, large_size * .8, type_name);
   EXPECT_EQ(new_ptr, ptr);
-  EXPECT_EQ(0U, *ReservationOffsetPointer(address));
-  EXPECT_EQ(1U, *ReservationOffsetPointer(address + kSuperPageSize));
-  EXPECT_EQ(2U, *ReservationOffsetPointer(address + kSuperPageSize * 2));
-  EXPECT_EQ(3U, *ReservationOffsetPointer(address + kSuperPageSize * 3));
-  EXPECT_EQ(4U, *ReservationOffsetPointer(address + kSuperPageSize * 4));
-  EXPECT_EQ(5U, *ReservationOffsetPointer(address + kSuperPageSize * 5));
+  EXPECT_EQ(0U, *table.GetOffsetPointerForTesting(address));
+  EXPECT_EQ(1U, *table.GetOffsetPointerForTesting(address + kSuperPageSize));
+  EXPECT_EQ(2U,
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 2));
+  EXPECT_EQ(3U,
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 3));
+  EXPECT_EQ(4U,
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 4));
+  EXPECT_EQ(5U,
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 5));
 
   allocator.root()->Free(ptr);
   // After free, the offsets must be kOffsetTagNotAllocated.
-  EXPECT_EQ(kOffsetTagNotAllocated, *ReservationOffsetPointer(address));
+  EXPECT_EQ(kOffsetTagNotAllocated, *table.GetOffsetPointerForTesting(address));
   EXPECT_EQ(kOffsetTagNotAllocated,
-            *ReservationOffsetPointer(address + kSuperPageSize));
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize));
   EXPECT_EQ(kOffsetTagNotAllocated,
-            *ReservationOffsetPointer(address + kSuperPageSize * 2));
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 2));
   EXPECT_EQ(kOffsetTagNotAllocated,
-            *ReservationOffsetPointer(address + kSuperPageSize * 3));
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 3));
   EXPECT_EQ(kOffsetTagNotAllocated,
-            *ReservationOffsetPointer(address + kSuperPageSize * 4));
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 4));
   EXPECT_EQ(kOffsetTagNotAllocated,
-            *ReservationOffsetPointer(address + kSuperPageSize * 5));
+            *table.GetOffsetPointerForTesting(address + kSuperPageSize * 5));
 }
 
 TEST_P(PartitionAllocTest, GetReservationStart) {
@@ -5369,13 +5384,16 @@
   uintptr_t reservation_start = slot_start - PartitionPageSize();
   EXPECT_EQ(0U, reservation_start & DirectMapAllocationGranularityOffsetMask());
 
+  ReservationOffsetTable table = allocator.root()->GetReservationOffsetTable();
+
   uintptr_t address = UntagPtr(ptr);
   for (uintptr_t a = address; a < address + large_size; ++a) {
-    uintptr_t address2 = GetDirectMapReservationStart(a) + PartitionPageSize();
+    uintptr_t address2 =
+        table.GetDirectMapReservationStart(a) + PartitionPageSize();
     EXPECT_EQ(slot_start, address2);
   }
 
-  EXPECT_EQ(reservation_start, GetDirectMapReservationStart(slot_start));
+  EXPECT_EQ(reservation_start, table.GetDirectMapReservationStart(slot_start));
 
   allocator.root()->Free(ptr);
 }
@@ -5386,11 +5404,12 @@
 #else
 TEST_P(PartitionAllocTest, CheckReservationType) {
 #endif  // PA_BUILDFLAG(IS_FUCHSIA)
+  ReservationOffsetTable table = allocator.root()->GetReservationOffsetTable();
   void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name);
   EXPECT_TRUE(ptr);
   uintptr_t address = UntagPtr(ptr);
   uintptr_t address_to_check = address;
-  EXPECT_FALSE(IsReservationStart(address_to_check));
+  EXPECT_FALSE(table.IsReservationStart(address_to_check));
   EXPECT_TRUE(
       IsManagedByNormalBucketsForTesting(address_to_check, allocator.root()));
   EXPECT_FALSE(
@@ -5398,7 +5417,7 @@
   EXPECT_TRUE(IsManagedByNormalBucketsOrDirectMapForTesting(address_to_check,
                                                             allocator.root()));
   address_to_check = address + kTestAllocSize - 1;
-  EXPECT_FALSE(IsReservationStart(address_to_check));
+  EXPECT_FALSE(table.IsReservationStart(address_to_check));
   EXPECT_TRUE(
       IsManagedByNormalBucketsForTesting(address_to_check, allocator.root()));
   EXPECT_FALSE(
@@ -5407,7 +5426,7 @@
                                                             allocator.root()));
   address_to_check =
       partition_alloc::internal::base::bits::AlignDown(address, kSuperPageSize);
-  EXPECT_TRUE(IsReservationStart(address_to_check));
+  EXPECT_TRUE(table.IsReservationStart(address_to_check));
   EXPECT_TRUE(
       IsManagedByNormalBucketsForTesting(address_to_check, allocator.root()));
   EXPECT_FALSE(
@@ -5418,7 +5437,7 @@
   // Freeing keeps a normal-bucket super page in memory.
   address_to_check =
       partition_alloc::internal::base::bits::AlignDown(address, kSuperPageSize);
-  EXPECT_TRUE(IsReservationStart(address_to_check));
+  EXPECT_TRUE(table.IsReservationStart(address_to_check));
   EXPECT_TRUE(
       IsManagedByNormalBucketsForTesting(address_to_check, allocator.root()));
   EXPECT_FALSE(
@@ -5432,7 +5451,7 @@
   EXPECT_TRUE(ptr);
   address = UntagPtr(ptr);
   address_to_check = address;
-  EXPECT_FALSE(IsReservationStart(address_to_check));
+  EXPECT_FALSE(table.IsReservationStart(address_to_check));
   EXPECT_FALSE(
       IsManagedByNormalBucketsForTesting(address_to_check, allocator.root()));
   EXPECT_TRUE(
@@ -5441,7 +5460,7 @@
                                                             allocator.root()));
   address_to_check =
       partition_alloc::internal::base::bits::AlignUp(address, kSuperPageSize);
-  EXPECT_FALSE(IsReservationStart(address_to_check));
+  EXPECT_FALSE(table.IsReservationStart(address_to_check));
   EXPECT_FALSE(
       IsManagedByNormalBucketsForTesting(address_to_check, allocator.root()));
   EXPECT_TRUE(
@@ -5449,7 +5468,7 @@
   EXPECT_TRUE(IsManagedByNormalBucketsOrDirectMapForTesting(address_to_check,
                                                             allocator.root()));
   address_to_check = address + large_size - 1;
-  EXPECT_FALSE(IsReservationStart(address_to_check));
+  EXPECT_FALSE(table.IsReservationStart(address_to_check));
   EXPECT_FALSE(
       IsManagedByNormalBucketsForTesting(address_to_check, allocator.root()));
   EXPECT_TRUE(
@@ -5458,7 +5477,7 @@
                                                             allocator.root()));
   address_to_check =
       partition_alloc::internal::base::bits::AlignDown(address, kSuperPageSize);
-  EXPECT_TRUE(IsReservationStart(address_to_check));
+  EXPECT_TRUE(table.IsReservationStart(address_to_check));
   EXPECT_FALSE(
       IsManagedByNormalBucketsForTesting(address_to_check, allocator.root()));
   EXPECT_TRUE(
@@ -5474,7 +5493,7 @@
 #if PA_BUILDFLAG(DCHECKS_ARE_ON) && \
     (!defined(OFFICIAL_BUILD) || PA_BUILDFLAG(IS_DEBUG))
   // Expect to DCHECK on unallocated region.
-  EXPECT_DEATH_IF_SUPPORTED(IsReservationStart(address_to_check), "");
+  EXPECT_DEATH_IF_SUPPORTED(table.IsReservationStart(address_to_check), "");
 #endif  //  PA_BUILDFLAG(DCHECKS_ARE_ON) && (!defined(OFFICIAL_BUILD) ||
         //  PA_BUILDFLAG(IS_DEBUG))
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.cc
index 5296fd7..80e056f 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.cc
@@ -330,18 +330,8 @@
     // so no other thread can update the same offset table entries at the
     // same time. Furthermore, nobody will be ready these offsets until this
     // function returns.
-    auto* offset_ptr = ReservationOffsetPointer(reservation_start);
-    [[maybe_unused]] const auto* offset_ptr_end =
-        GetReservationOffsetTableEnd(reservation_start);
-
-    // |raw_size| > MaxBucketed(). So |reservation_size| > 0.
-    PA_DCHECK(reservation_size > 0);
-    const uint16_t offset_end = (reservation_size - 1) >> kSuperPageShift;
-    for (uint16_t offset = 0; offset <= offset_end; ++offset) {
-      PA_DCHECK(offset < kOffsetTagNormalBuckets);
-      PA_DCHECK(offset_ptr < offset_ptr_end);
-      *offset_ptr++ = offset;
-    }
+    root->GetReservationOffsetTable().SetDirectMapReservationStart(
+        reservation_start, reservation_size);
 
     auto* super_page_extent = PartitionSuperPageToExtent(reservation_start);
     auto* writable_super_page_extent = super_page_extent->ToWritable(root);
@@ -815,7 +805,7 @@
 PartitionBucket::InitializeSuperPage(PartitionRoot* root,
                                      uintptr_t super_page,
                                      uintptr_t requested_address) {
-  *ReservationOffsetPointer(super_page) = kOffsetTagNormalBuckets;
+  root->GetReservationOffsetTable().SetNormalBucketsTag(super_page);
 
   root->total_size_of_super_pages.fetch_add(kSuperPageSize,
                                             std::memory_order_relaxed);
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_page.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_page.cc
index 56bd0d36..ce4caa0 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_page.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_page.cc
@@ -30,7 +30,7 @@
 
 void UnmapNow(uintptr_t reservation_start,
               size_t reservation_size,
-              pool_handle pool);
+              PartitionRoot* root);
 
 PA_ALWAYS_INLINE void PartitionDirectUnmap(
     SlotSpanMetadata<MetadataKind::kReadOnly>* slot_span) {
@@ -78,7 +78,7 @@
   // while releasing the address space.
   ScopedUnlockGuard unlock{PartitionRootLock(root)};
   ScopedSyscallTimer timer{root};
-  UnmapNow(reservation_start, reservation_size, root->ChoosePool());
+  UnmapNow(reservation_start, reservation_size, root);
 }
 
 }  // namespace
@@ -341,8 +341,10 @@
 
 void UnmapNow(uintptr_t reservation_start,
               size_t reservation_size,
-              pool_handle pool) {
+              PartitionRoot* root) {
   PA_DCHECK(reservation_start && reservation_size > 0);
+  pool_handle pool = root->ChoosePool();
+
 #if PA_BUILDFLAG(DCHECKS_ARE_ON)
   // When ENABLE_BACKUP_REF_PTR_SUPPORT is off, BRP pool isn't used.
 #if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
@@ -384,21 +386,13 @@
   }
 #endif  // PA_BUILDFLAG(DCHECKS_ARE_ON)
 
-  PA_DCHECK((reservation_start & kSuperPageOffsetMask) == 0);
-  uintptr_t reservation_end = reservation_start + reservation_size;
-  auto* offset_ptr = ReservationOffsetPointer(reservation_start);
   // Reset the offset table entries for the given memory before unreserving
   // it. Since the memory is not unreserved and not available for other
   // threads, the table entries for the memory are not modified by other
   // threads either. So we can update the table entries without race
   // condition.
-  uint16_t i = 0;
-  for (uintptr_t address = reservation_start; address < reservation_end;
-       address += kSuperPageSize) {
-    PA_DCHECK(offset_ptr < GetReservationOffsetTableEnd(address));
-    PA_DCHECK(*offset_ptr == i++);
-    *offset_ptr++ = kOffsetTagNotAllocated;
-  }
+  root->GetReservationOffsetTable().SetNotAllocatedTag(reservation_start,
+                                                       reservation_size);
 
 #if PA_CONFIG(ENABLE_SHADOW_METADATA)
   // UnmapShadowMetadata must be done before unreserving memory, because
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_page.h b/base/allocator/partition_allocator/src/partition_alloc/partition_page.h
index 8bac467..48a8c5e0 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_page.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_page.h
@@ -524,7 +524,8 @@
 PartitionSuperPageToMetadataArea(uintptr_t super_page) {
   // This can't be just any super page, but it has to be the first super page of
   // the reservation, as we assume here that the metadata is near its beginning.
-  PA_DCHECK(IsReservationStart(super_page));
+  PA_DCHECK(
+      ReservationOffsetTable::Get(super_page).IsReservationStart(super_page));
   PA_DCHECK(!(super_page & kSuperPageOffsetMask));
   // The metadata area is exactly one system page (the guard page) into the
   // super page.
@@ -557,15 +558,13 @@
   return 0ull;
 }
 
-PA_ALWAYS_INLINE uintptr_t
-SuperPagePayloadStartOffset(bool is_managed_by_normal_buckets) {
+PA_ALWAYS_INLINE uintptr_t SuperPagePayloadStartOffset() {
   return PartitionPageSize();
 }
 
 PA_ALWAYS_INLINE uintptr_t SuperPagePayloadBegin(uintptr_t super_page) {
   PA_DCHECK(!(super_page % kSuperPageAlignment));
-  return super_page +
-         SuperPagePayloadStartOffset(IsManagedByNormalBuckets(super_page));
+  return super_page + SuperPagePayloadStartOffset();
 }
 
 PA_ALWAYS_INLINE uintptr_t SuperPagePayloadEndOffset() {
@@ -626,7 +625,8 @@
   uintptr_t super_page = address & kSuperPageBaseMask;
 
 #if PA_BUILDFLAG(DCHECKS_ARE_ON)
-  PA_DCHECK(IsReservationStart(super_page));
+  PA_DCHECK(
+      ReservationOffsetTable::Get(super_page).IsReservationStart(super_page));
   PA_DCHECK(IsWithinSuperPagePayload(address));
 #endif  // PA_BUILDFLAG(DCHECKS_ARE_ON)
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
index 6755bdf0..71d501e 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
@@ -69,7 +69,8 @@
 PtrPosWithinAlloc IsPtrWithinSameAlloc(uintptr_t orig_address,
                                        uintptr_t test_address,
                                        size_t type_size) {
-  PA_DCHECK(IsManagedByNormalBucketsOrDirectMap(orig_address));
+  PA_DCHECK(ReservationOffsetTable::Get(orig_address)
+                .IsManagedByNormalBucketsOrDirectMap(orig_address));
   DCheckIfManagedByPartitionAllocBRPPool(orig_address);
 
   auto [slot_start, _] =
@@ -1004,24 +1005,8 @@
           reinterpret_cast<uintptr_t>(curr), kSuperPageSize);
       size_t reservation_size = curr->reservation_size;
 
-      {
-        uintptr_t reservation_end = reservation_start + reservation_size;
-        auto* offset_ptr =
-            internal::ReservationOffsetPointer(reservation_start);
-        // Reset the offset table entries for the given memory before
-        // unreserving it. Since the memory is not unreserved and not available
-        // for other threads, the table entries for the memory are not modified
-        // by other threads either. So we can update the table entries without
-        // race condition.
-        uint16_t i = 0;
-        for (uintptr_t address = reservation_start; address < reservation_end;
-             address += kSuperPageSize) {
-          PA_DCHECK(offset_ptr <
-                    internal::GetReservationOffsetTableEnd(address));
-          PA_DCHECK(*offset_ptr == i++);
-          *offset_ptr++ = internal::kOffsetTagNotAllocated;
-        }
-      }
+      GetReservationOffsetTable().SetNotAllocatedTag(reservation_start,
+                                                     reservation_size);
 #if !PA_BUILDFLAG(HAS_64_BIT_POINTERS)
       internal::AddressPoolManager::GetInstance().MarkUnused(
           pool_handle, reservation_start, reservation_size);
@@ -1222,6 +1207,8 @@
     settings.offset_lookup =
         internal::PartitionAddressSpace::GetOffsetLookup(settings.pool_handle);
 #endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+    settings.reservation_offset_table =
+        internal::ReservationOffsetTable::Get(settings.pool_handle);
 
     // We mark the sentinel slot span as free to make sure it is skipped by our
     // logic to find a new active slot span.
@@ -1353,8 +1340,8 @@
     size_t requested_size) {
   PA_DCHECK(slot_span->bucket->is_direct_mapped());
   // Slot-span metadata isn't MTE-tagged.
-  PA_DCHECK(
-      internal::IsManagedByDirectMap(reinterpret_cast<uintptr_t>(slot_span)));
+  PA_DCHECK(GetReservationOffsetTable().IsManagedByDirectMap(
+      reinterpret_cast<uintptr_t>(slot_span)));
 
   size_t raw_size = AdjustSizeForExtrasAdd(requested_size);
   auto* extent = ReadOnlyDirectMapExtent::FromSlotSpanMetadata(slot_span);
@@ -1403,7 +1390,7 @@
       PartitionRoot::GetDirectMapMetadataAndGuardPagesSize();
 #if PA_BUILDFLAG(DCHECKS_ARE_ON)
   uintptr_t reservation_start = slot_start & internal::kSuperPageBaseMask;
-  PA_DCHECK(internal::IsReservationStart(reservation_start));
+  PA_DCHECK(GetReservationOffsetTable().IsReservationStart(reservation_start));
   PA_DCHECK(slot_start + available_reservation_size ==
             reservation_start + current_reservation_size -
                 GetDirectMapMetadataAndGuardPagesSize() +
@@ -1483,7 +1470,7 @@
     internal::SlotSpanMetadata<internal::MetadataKind::kReadOnly>* slot_span,
     size_t new_size) {
   uintptr_t slot_start = ObjectToSlotStart(object);
-  PA_DCHECK(internal::IsManagedByNormalBuckets(slot_start));
+  PA_DCHECK(GetReservationOffsetTable().IsManagedByNormalBuckets(slot_start));
 
   // TODO: note that tcmalloc will "ignore" a downsizing realloc() unless the
   // new size is a significant percentage smaller. We could do the same if we
@@ -2012,15 +1999,19 @@
   if (!IsManagedByPartitionAlloc(address)) {
     // Not managed by PA; cannot help to determine its integrity.
     return;
-  } else if (internal::IsManagedByDirectMap(address)) {
+  }
+
+  auto* root = FromAddrInFirstSuperpage(address);
+
+  const internal::ReservationOffsetTable& reservation_offset =
+      root->GetReservationOffsetTable();
+  if (reservation_offset.IsManagedByDirectMap(address)) {
     // OOB for direct-mapped allocations is likely immediate crash.
     // No extra benefit from additional checks.
     return;
   }
-  PA_CHECK(internal::IsManagedByNormalBuckets(address));
 
-  auto* root = FromAddrInFirstSuperpage(address);
-
+  PA_CHECK(reservation_offset.IsManagedByNormalBuckets(address));
   ReadOnlySlotSpanMetadata* slot_span =
       ReadOnlySlotSpanMetadata::FromAddr(address);
   PA_CHECK(PartitionRoot::FromSlotSpanMetadata(slot_span) == root);
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_root.h b/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
index 6dc8bb9..bd53f05 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
@@ -270,6 +270,7 @@
 #if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
     internal::PoolOffsetLookup offset_lookup;
 #endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+    internal::ReservationOffsetTable reservation_offset_table;
 
     bool eventually_zero_freed_memory = false;
     internal::SchedulerLoopQuarantineConfig
@@ -721,6 +722,10 @@
     return settings.offset_lookup;
   }
 #endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+  PA_ALWAYS_INLINE const internal::ReservationOffsetTable&
+  GetReservationOffsetTable() const {
+    return settings.reservation_offset_table;
+  }
 
   PA_ALWAYS_INLINE static PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
   GetDirectMapMetadataAndGuardPagesSize() {
@@ -1105,15 +1110,9 @@
 PA_ALWAYS_INLINE SlotAddressAndSize
 PartitionAllocGetDirectMapSlotStartAndSizeInBRPPool(uintptr_t address) {
   PA_DCHECK(IsManagedByPartitionAllocBRPPool(address));
-#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-  // Use this variant of GetDirectMapReservationStart as it has better
-  // performance.
-  uintptr_t offset = OffsetInBRPPool(address);
   uintptr_t reservation_start =
-      GetDirectMapReservationStart(address, kBRPPoolHandle, offset);
-#else  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-  uintptr_t reservation_start = GetDirectMapReservationStart(address);
-#endif
+      ReservationOffsetTable::Get(pool_handle::kBRPPoolHandle)
+          .GetDirectMapReservationStart(address);
   if (!reservation_start) {
     return SlotAddressAndSize{.slot_start = uintptr_t(0), .size = size_t(0)};
   }
@@ -1156,7 +1155,9 @@
 // the in-slot metadata is in place for this allocation.
 PA_ALWAYS_INLINE SlotAddressAndSize
 PartitionAllocGetSlotStartAndSizeInBRPPool(uintptr_t address) {
-  PA_DCHECK(IsManagedByNormalBucketsOrDirectMap(address));
+  PA_DCHECK(
+      ReservationOffsetTable::Get(address).IsManagedByNormalBucketsOrDirectMap(
+          address));
   DCheckIfManagedByPartitionAllocBRPPool(address);
 
   auto directmap_slot_info =
@@ -1766,7 +1767,8 @@
 
 PA_ALWAYS_INLINE PartitionRoot* PartitionRoot::FromFirstSuperPage(
     uintptr_t super_page) {
-  PA_DCHECK(internal::IsReservationStart(super_page));
+  PA_DCHECK(internal::ReservationOffsetTable::Get(super_page)
+                .IsReservationStart(super_page));
   auto* extent_entry = internal::PartitionSuperPageToExtent(super_page);
   PartitionRoot* root = extent_entry->root;
   PA_DCHECK(root->inverted_self == ~reinterpret_cast<uintptr_t>(root));
@@ -1776,7 +1778,8 @@
 PA_ALWAYS_INLINE PartitionRoot* PartitionRoot::FromAddrInFirstSuperpage(
     uintptr_t address) {
   uintptr_t super_page = address & internal::kSuperPageBaseMask;
-  PA_DCHECK(internal::IsReservationStart(super_page));
+  PA_DCHECK(internal::ReservationOffsetTable::Get(super_page)
+                .IsReservationStart(super_page));
   return FromFirstSuperPage(super_page);
 }
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_superpage_extent_entry.h b/base/allocator/partition_allocator/src/partition_alloc/partition_superpage_extent_entry.h
index e855e5a..465068a 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_superpage_extent_entry.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_superpage_extent_entry.h
@@ -143,7 +143,8 @@
     const PartitionSuperPageExtentEntry<MetadataKind::kReadOnly>* extent) {
   PA_DCHECK(0 < extent->number_of_consecutive_super_pages);
   uintptr_t extent_as_uintptr = reinterpret_cast<uintptr_t>(extent);
-  PA_DCHECK(IsManagedByNormalBuckets(extent_as_uintptr));
+  PA_DCHECK(ReservationOffsetTable::Get(extent_as_uintptr)
+                .IsManagedByNormalBuckets(extent_as_uintptr));
   return base::bits::AlignDown(extent_as_uintptr, kSuperPageAlignment);
 }
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.cc b/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.cc
index 67eb234..46a2909 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.cc
@@ -125,13 +125,15 @@
 #if PA_BUILDFLAG(DCHECKS_ARE_ON) || \
     PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)
 void CheckThatAddressIsntWithinFirstPartitionPage(uintptr_t address) {
-  if (partition_alloc::internal::IsManagedByDirectMap(address)) {
+  auto reservation_offset_table =
+      partition_alloc::internal::ReservationOffsetTable::Get(address);
+  if (reservation_offset_table.IsManagedByDirectMap(address)) {
     uintptr_t reservation_start =
-        partition_alloc::internal::GetDirectMapReservationStart(address);
+        reservation_offset_table.GetDirectMapReservationStart(address);
     PA_BASE_CHECK(address - reservation_start >=
                   partition_alloc::PartitionPageSize());
   } else {
-    PA_BASE_CHECK(partition_alloc::internal::IsManagedByNormalBuckets(address));
+    PA_BASE_CHECK(reservation_offset_table.IsManagedByNormalBuckets(address));
     PA_BASE_CHECK(address % partition_alloc::kSuperPageSize >=
                   partition_alloc::PartitionPageSize());
   }
diff --git a/base/allocator/partition_allocator/src/partition_alloc/reservation_offset_table.cc b/base/allocator/partition_allocator/src/partition_alloc/reservation_offset_table.cc
index 34b496f..2ba30bd 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/reservation_offset_table.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/reservation_offset_table.cc
@@ -9,9 +9,28 @@
 namespace partition_alloc::internal {
 
 #if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-PA_CONSTINIT ReservationOffsetTable ReservationOffsetTable::singleton_;
+PA_CONSTINIT ReservationOffsetTable::_ReservationOffsetTable<
+    ReservationOffsetTable::kRegularOffsetTableLength>
+    ReservationOffsetTable::regular_pool_table_;
+PA_CONSTINIT ReservationOffsetTable::_ReservationOffsetTable<
+    ReservationOffsetTable::kBRPOffsetTableLength>
+    ReservationOffsetTable::brp_pool_table_;
+PA_CONSTINIT ReservationOffsetTable::_ReservationOffsetTable<
+    ReservationOffsetTable::kConfigurableOffsetTableLength>
+    ReservationOffsetTable::configurable_pool_table_;
+#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
+// If thread isolation support is enabled, we need to write-protect the tables
+// of the thread isolated pool. For this, the thread isolated ones start on a
+// page boundary.
+PA_THREAD_ISOLATED_ALIGN
+PA_CONSTINIT ReservationOffsetTable::_ReservationOffsetTable<
+    ReservationOffsetTable::kThreadIsolatedOffsetTableLength>
+    ReservationOffsetTable::thread_isolated_pool_table_;
+#endif
 #else
-PA_CONSTINIT ReservationOffsetTable::_ReservationOffsetTable
+// A single table for the entire 32-bit address space.
+PA_CONSTINIT ReservationOffsetTable::_ReservationOffsetTable<
+    ReservationOffsetTable::kReservationOffsetTableLength>
     ReservationOffsetTable::reservation_offset_table_;
 #endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/reservation_offset_table.h b/base/allocator/partition_allocator/src/partition_alloc/reservation_offset_table.h
index 463912b..1a467af 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/reservation_offset_table.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/reservation_offset_table.h
@@ -21,12 +21,6 @@
 #include "partition_alloc/thread_isolation/alignment.h"
 
 namespace partition_alloc::internal {
-
-static constexpr uint16_t kOffsetTagNotAllocated =
-    std::numeric_limits<uint16_t>::max();
-static constexpr uint16_t kOffsetTagNormalBuckets =
-    std::numeric_limits<uint16_t>::max() - 1;
-
 // The main purpose of the reservation offset table is to easily locate the
 // direct map reservation start address for any given address. There is one
 // entry in the table for each super page.
@@ -62,31 +56,53 @@
 //    to further determine which part of the super page is used by
 //    PartitionAlloc. This isn't a problem in 64-bit mode, where allocation
 //    granularity is kSuperPageSize.
-class PA_COMPONENT_EXPORT(PARTITION_ALLOC)
-    PA_THREAD_ISOLATED_ALIGN ReservationOffsetTable {
- public:
+class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ReservationOffsetTable {
+  static constexpr uint16_t kOffsetTagNotAllocated =
+      std::numeric_limits<uint16_t>::max();
+  static constexpr uint16_t kOffsetTagNormalBuckets =
+      std::numeric_limits<uint16_t>::max() - 1;
+
 #if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
   // There is one reservation offset table per Pool in 64-bit mode.
-  static constexpr size_t kReservationOffsetTableCoverage = kPoolMaxSize;
-  static constexpr size_t kReservationOffsetTableLength =
-      kReservationOffsetTableCoverage >> kSuperPageShift;
+  static constexpr size_t kRegularOffsetTableLength =
+      PartitionAddressSpace::CorePoolMaxSize() >> kSuperPageShift;
+  static_assert(kRegularOffsetTableLength < kOffsetTagNormalBuckets,
+                "Offsets should be smaller than kOffsetTagNormalBuckets.");
+  static constexpr size_t kBRPOffsetTableLength =
+      PartitionAddressSpace::CorePoolMaxSize() >> kSuperPageShift;
+  static_assert(kBRPOffsetTableLength < kOffsetTagNormalBuckets,
+                "Offsets should be smaller than kOffsetTagNormalBuckets.");
+  static constexpr size_t kConfigurableOffsetTableLength =
+      PartitionAddressSpace::ConfigurablePoolMaxSize() >> kSuperPageShift;
+  static_assert(kConfigurableOffsetTableLength < kOffsetTagNormalBuckets,
+                "Offsets should be smaller than kOffsetTagNormalBuckets.");
+#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
+ public:
+  static constexpr size_t kThreadIsolatedOffsetTableLength =
+      PartitionAddressSpace::ThreadIsolatedPoolSize() >> kSuperPageShift;
+  static_assert(kThreadIsolatedOffsetTableLength < kOffsetTagNormalBuckets,
+                "Offsets should be smaller than kOffsetTagNormalBuckets.");
+
+ private:
+#endif  // PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
 #else
   // The size of the reservation offset table should cover the entire 32-bit
   // address space, one element per super page.
   static constexpr uint64_t kGiB = 1024 * 1024 * 1024ull;
   static constexpr size_t kReservationOffsetTableLength =
       4 * kGiB / kSuperPageSize;
-#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
   static_assert(kReservationOffsetTableLength < kOffsetTagNormalBuckets,
                 "Offsets should be smaller than kOffsetTagNormalBuckets.");
+#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
 
+  template <size_t length>
   struct _ReservationOffsetTable {
     // The number of table elements is less than MAX_UINT16, so the element type
     // can be uint16_t.
     static_assert(
-        kReservationOffsetTableLength <= std::numeric_limits<uint16_t>::max(),
+        length <= std::numeric_limits<uint16_t>::max(),
         "Length of the reservation offset table must be less than MAX_UINT16");
-    uint16_t offsets[kReservationOffsetTableLength] = {};
+    uint16_t offsets[length] = {};
 
     constexpr _ReservationOffsetTable() {
       for (uint16_t& offset : offsets) {
@@ -94,197 +110,210 @@
       }
     }
   };
-#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-  // If thread isolation support is enabled, we need to write-protect the tables
-  // of the thread isolated pool. For this, we need to pad the tables so that
-  // the thread isolated ones start on a page boundary.
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wzero-length-array"
-#endif
-  char pad_[PA_THREAD_ISOLATED_ARRAY_PAD_SZ(_ReservationOffsetTable,
-                                            kNumPools)] = {};
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
 
-  struct _ReservationOffsetTable tables[kNumPools];
-  PA_CONSTINIT static ReservationOffsetTable singleton_;
+  template <size_t length>
+  PA_ALWAYS_INLINE explicit ReservationOffsetTable(
+      _ReservationOffsetTable<length>& table
+#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+      ,
+      PoolOffsetLookup offset_lookup
+#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+      )
+      : table_begin_(table.offsets)
+#if PA_BUILDFLAG(DCHECKS_ARE_ON)
+        ,
+        table_end_(table.offsets + length)
+#endif  // PA_BUILDFLAG(DCHECKS_ARE_ON)
+#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+        ,
+        offset_lookup_(offset_lookup)
+#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+  {
+  }
+
+ public:
+  PA_ALWAYS_INLINE ReservationOffsetTable() = default;
+
+  PA_ALWAYS_INLINE static ReservationOffsetTable Get(pool_handle handle) {
+#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+    PoolOffsetLookup offset_lookup =
+        PartitionAddressSpace::GetOffsetLookup(handle);
+    switch (handle) {
+      case kRegularPoolHandle:
+        return ReservationOffsetTable(regular_pool_table_, offset_lookup);
+      case kBRPPoolHandle:
+        return ReservationOffsetTable(brp_pool_table_, offset_lookup);
+      case kConfigurablePoolHandle:
+        return ReservationOffsetTable(configurable_pool_table_, offset_lookup);
+#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
+      case kThreadIsolatedPoolHandle:
+        return ReservationOffsetTable(thread_isolated_pool_table_,
+                                      offset_lookup);
+#endif  // PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
+      default:
+        PA_NOTREACHED();
+    }
+#else
+    return ReservationOffsetTable(reservation_offset_table_);
+#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+  }
+
+  PA_ALWAYS_INLINE static ReservationOffsetTable Get(uintptr_t address) {
+#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+    return ReservationOffsetTable::Get(
+        PartitionAddressSpace::GetPoolInfo(address).handle);
+#else
+    return ReservationOffsetTable(reservation_offset_table_);
+#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+  }
+
+  PA_ALWAYS_INLINE void* GetData() const { return table_begin_; }
+
+  PA_ALWAYS_INLINE void SetNotAllocatedTag(uintptr_t reservation_start,
+                                           size_t reservation_size = 1) const {
+    PA_DCHECK((reservation_start & kSuperPageOffsetMask) == 0);
+    PA_DCHECK(reservation_size > 0);
+    uint16_t* offset_ptr = GetOffsetPointer(reservation_start);
+
+    PA_DCHECK((reservation_size - 1) >> kSuperPageShift <=
+              std::numeric_limits<uint16_t>::max());
+    const uint16_t offset_end =
+        static_cast<uint16_t>((reservation_size - 1) >> kSuperPageShift);
+    for (uint16_t offset = 0; offset <= offset_end; ++offset) {
+#if PA_BUILDFLAG(DCHECKS_ARE_ON)
+      PA_DCHECK(offset_ptr < table_end_);
+#endif  // PA_BUILDFLAG(DCHECKS_ARE_ON)
+      *offset_ptr++ = kOffsetTagNotAllocated;
+    }
+  }
+
+  PA_ALWAYS_INLINE void SetNormalBucketsTag(uintptr_t reservation_start) const {
+    PA_DCHECK((reservation_start & kSuperPageOffsetMask) == 0);
+    *GetOffsetPointer(reservation_start) = kOffsetTagNormalBuckets;
+  }
+
+  PA_ALWAYS_INLINE void SetDirectMapReservationStart(
+      uintptr_t reservation_start,
+      size_t reservation_size) const {
+    PA_DCHECK((reservation_start & kSuperPageOffsetMask) == 0);
+    PA_DCHECK(reservation_size > 0);
+    uint16_t* offset_ptr = GetOffsetPointer(reservation_start);
+
+    PA_DCHECK((reservation_size - 1) >> kSuperPageShift <=
+              std::numeric_limits<uint16_t>::max());
+    const uint16_t offset_end =
+        static_cast<uint16_t>((reservation_size - 1) >> kSuperPageShift);
+    for (uint16_t offset = 0; offset <= offset_end; ++offset) {
+      PA_DCHECK(offset < kOffsetTagNormalBuckets);
+#if PA_BUILDFLAG(DCHECKS_ARE_ON)
+      PA_DCHECK(offset_ptr < table_end_);
+#endif  // PA_BUILDFLAG(DCHECKS_ARE_ON)
+      *offset_ptr++ = offset;
+    }
+  }
+
+  // If the given address doesn't point to direct-map allocated memory,
+  // returns 0.
+  PA_ALWAYS_INLINE uintptr_t GetDirectMapReservationStart(uintptr_t address) {
+    uint16_t* offset_ptr = GetOffsetPointer(address);
+    PA_DCHECK(*offset_ptr != kOffsetTagNotAllocated);
+    if (*offset_ptr == kOffsetTagNormalBuckets) {
+      return 0;
+    }
+    uintptr_t reservation_start =
+        (address & kSuperPageBaseMask) -
+        (static_cast<size_t>(*offset_ptr) << kSuperPageShift);
+
+#if PA_BUILDFLAG(DCHECKS_ARE_ON)
+#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+    PA_DCHECK(offset_lookup_.Includes(reservation_start));
+#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+    PA_DCHECK(*GetOffsetPointer(reservation_start) == 0);
+#endif  // PA_BUILDFLAG(DCHECKS_ARE_ON)
+
+    return reservation_start;
+  }
+
+  // Returns true if |address| is the beginning of the first super page of a
+  // reservation, i.e. either a normal bucket super page, or the first super
+  // page of direct map. |address| must belong to an allocated super page.
+  PA_ALWAYS_INLINE bool IsReservationStart(uintptr_t address) const {
+    uint16_t* offset_ptr = GetOffsetPointer(address);
+    PA_DCHECK(*offset_ptr != kOffsetTagNotAllocated);
+    return ((*offset_ptr == kOffsetTagNormalBuckets) || (*offset_ptr == 0)) &&
+           (address % kSuperPageSize == 0);
+  }
+
+  // Returns true if |address| belongs to a normal bucket super page.
+  PA_ALWAYS_INLINE bool IsManagedByNormalBuckets(uintptr_t address) const {
+    uint16_t* offset_ptr = GetOffsetPointer(address);
+    return *offset_ptr == kOffsetTagNormalBuckets;
+  }
+
+  // Returns true if |address| belongs to a direct map region.
+  PA_ALWAYS_INLINE bool IsManagedByDirectMap(uintptr_t address) const {
+    uint16_t* offset_ptr = GetOffsetPointer(address);
+    return *offset_ptr != kOffsetTagNormalBuckets &&
+           *offset_ptr != kOffsetTagNotAllocated;
+  }
+
+  // Returns true if |address| belongs to a normal bucket super page or a direct
+  // map region, i.e. belongs to an allocated super page.
+  PA_ALWAYS_INLINE bool IsManagedByNormalBucketsOrDirectMap(
+      uintptr_t address) const {
+    uint16_t* offset_ptr = GetOffsetPointer(address);
+    return *offset_ptr != kOffsetTagNotAllocated;
+  }
+
+  PA_ALWAYS_INLINE uint16_t* GetOffsetPointerForTesting(
+      uintptr_t address) const {
+    return GetOffsetPointer(address);
+  }
+
+ private:
+  PA_ALWAYS_INLINE uint16_t* GetOffsetPointer(uintptr_t address) const {
+#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+    size_t table_index = offset_lookup_.GetOffset(address) >> kSuperPageShift;
+#else
+    size_t table_index = address >> kSuperPageShift;
+#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+    uint16_t* offset_ptr = &table_begin_[table_index];
+#if PA_BUILDFLAG(DCHECKS_ARE_ON)
+    PA_DCHECK(offset_ptr < table_end_);
+#endif  // PA_BUILDFLAG(DCHECKS_ARE_ON)
+    return offset_ptr;
+  }
+
+  uint16_t* table_begin_ = nullptr;
+#if PA_BUILDFLAG(DCHECKS_ARE_ON)
+  uint16_t* table_end_ = nullptr;
+#endif
+#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+  PoolOffsetLookup offset_lookup_;
+#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+
+#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
+  PA_CONSTINIT static _ReservationOffsetTable<kRegularOffsetTableLength>
+      regular_pool_table_;
+  PA_CONSTINIT static _ReservationOffsetTable<kBRPOffsetTableLength>
+      brp_pool_table_;
+  PA_CONSTINIT static _ReservationOffsetTable<kConfigurableOffsetTableLength>
+      configurable_pool_table_;
+#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
+  // If thread isolation support is enabled, we need to write-protect the tables
+  // of the thread isolated pool. For this, the thread isolated ones start on a
+  // page boundary.
+  PA_THREAD_ISOLATED_ALIGN
+  PA_CONSTINIT static _ReservationOffsetTable<kThreadIsolatedOffsetTableLength>
+      thread_isolated_pool_table_;
+#endif
 #else
   // A single table for the entire 32-bit address space.
-  PA_CONSTINIT static struct _ReservationOffsetTable reservation_offset_table_;
+  PA_CONSTINIT static _ReservationOffsetTable<kReservationOffsetTableLength>
+      reservation_offset_table_;
 #endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
 };
 
-#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-PA_ALWAYS_INLINE uint16_t* GetReservationOffsetTable(pool_handle handle) {
-  PA_DCHECK(kNullPoolHandle < handle && handle <= kNumPools);
-  return ReservationOffsetTable::singleton_.tables[handle - 1].offsets;
-}
-
-PA_ALWAYS_INLINE const uint16_t* GetReservationOffsetTableEnd(
-    pool_handle handle) {
-  return GetReservationOffsetTable(handle) +
-         ReservationOffsetTable::kReservationOffsetTableLength;
-}
-
-PA_ALWAYS_INLINE uint16_t* GetReservationOffsetTable(uintptr_t address) {
-  pool_handle handle = GetPool(address);
-  return GetReservationOffsetTable(handle);
-}
-
-PA_ALWAYS_INLINE const uint16_t* GetReservationOffsetTableEnd(
-    uintptr_t address) {
-  pool_handle handle = GetPool(address);
-  return GetReservationOffsetTableEnd(handle);
-}
-
-PA_ALWAYS_INLINE uint16_t* ReservationOffsetPointer(pool_handle pool,
-                                                    uintptr_t offset_in_pool) {
-  size_t table_index = offset_in_pool >> kSuperPageShift;
-  PA_DCHECK(table_index <
-            ReservationOffsetTable::kReservationOffsetTableLength);
-  return GetReservationOffsetTable(pool) + table_index;
-}
-#else   // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-PA_ALWAYS_INLINE uint16_t* GetReservationOffsetTable(uintptr_t address) {
-  return ReservationOffsetTable::reservation_offset_table_.offsets;
-}
-
-PA_ALWAYS_INLINE const uint16_t* GetReservationOffsetTableEnd(
-    uintptr_t address) {
-  return ReservationOffsetTable::reservation_offset_table_.offsets +
-         ReservationOffsetTable::kReservationOffsetTableLength;
-}
-#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-
-PA_ALWAYS_INLINE uint16_t* ReservationOffsetPointer(uintptr_t address) {
-#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-  // In 64-bit mode, find the owning Pool and compute the offset from its base.
-  PartitionAddressSpace::PoolInfo info = GetPoolInfo(address);
-  return ReservationOffsetPointer(info.handle, info.offset);
-#else
-  size_t table_index = address >> kSuperPageShift;
-  PA_DCHECK(table_index <
-            ReservationOffsetTable::kReservationOffsetTableLength);
-  return GetReservationOffsetTable(address) + table_index;
-#endif
-}
-
-PA_ALWAYS_INLINE uintptr_t ComputeReservationStart(uintptr_t address,
-                                                   uint16_t* offset_ptr) {
-  return (address & kSuperPageBaseMask) -
-         (static_cast<size_t>(*offset_ptr) << kSuperPageShift);
-}
-
-// If the given address doesn't point to direct-map allocated memory,
-// returns 0.
-PA_ALWAYS_INLINE uintptr_t GetDirectMapReservationStart(uintptr_t address) {
-#if PA_BUILDFLAG(DCHECKS_ARE_ON)
-  bool is_in_brp_pool = IsManagedByPartitionAllocBRPPool(address);
-  bool is_in_regular_pool = IsManagedByPartitionAllocRegularPool(address);
-  bool is_in_configurable_pool =
-      IsManagedByPartitionAllocConfigurablePool(address);
-#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
-  bool is_in_thread_isolated_pool =
-      IsManagedByPartitionAllocThreadIsolatedPool(address);
-#endif
-
-  // When ENABLE_BACKUP_REF_PTR_SUPPORT is off, BRP pool isn't used.
-#if !PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
-  PA_DCHECK(!is_in_brp_pool);
-#endif
-#endif  // PA_BUILDFLAG(DCHECKS_ARE_ON)
-  uint16_t* offset_ptr = ReservationOffsetPointer(address);
-  PA_DCHECK(*offset_ptr != kOffsetTagNotAllocated);
-  if (*offset_ptr == kOffsetTagNormalBuckets) {
-    return 0;
-  }
-  uintptr_t reservation_start = ComputeReservationStart(address, offset_ptr);
-#if PA_BUILDFLAG(DCHECKS_ARE_ON)
-  // MSVC workaround: the preprocessor seems to choke on an `#if` embedded
-  // inside another macro (PA_DCHECK).
-#if !PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-  constexpr size_t kBRPOffset =
-      AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap *
-      AddressPoolManagerBitmap::kGuardOffsetOfBRPPoolBitmap;
-#else
-  constexpr size_t kBRPOffset = 0ull;
-#endif  // !PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-  // Make sure the reservation start is in the same pool as |address|.
-  // In the 32-bit mode, the beginning of a reservation may be excluded
-  // from the BRP pool, so shift the pointer. The other pools don't have
-  // this logic.
-  PA_DCHECK(is_in_brp_pool ==
-            IsManagedByPartitionAllocBRPPool(reservation_start + kBRPOffset));
-  PA_DCHECK(is_in_regular_pool ==
-            IsManagedByPartitionAllocRegularPool(reservation_start));
-  PA_DCHECK(is_in_configurable_pool ==
-            IsManagedByPartitionAllocConfigurablePool(reservation_start));
-#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
-  PA_DCHECK(is_in_thread_isolated_pool ==
-            IsManagedByPartitionAllocThreadIsolatedPool(reservation_start));
-#endif
-  PA_DCHECK(*ReservationOffsetPointer(reservation_start) == 0);
-#endif  // PA_BUILDFLAG(DCHECKS_ARE_ON)
-
-  return reservation_start;
-}
-
-#if PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-// If the given address doesn't point to direct-map allocated memory,
-// returns 0.
-// This variant has better performance than the regular one on 64-bit builds if
-// the Pool that an allocation belongs to is known.
-PA_ALWAYS_INLINE uintptr_t
-GetDirectMapReservationStart(uintptr_t address,
-                             pool_handle pool,
-                             uintptr_t offset_in_pool) {
-  PA_DCHECK(AddressPoolManager::GetInstance().GetPoolBaseAddress(pool) +
-                offset_in_pool ==
-            address);
-  uint16_t* offset_ptr = ReservationOffsetPointer(pool, offset_in_pool);
-  PA_DCHECK(*offset_ptr != kOffsetTagNotAllocated);
-  if (*offset_ptr == kOffsetTagNormalBuckets) {
-    return 0;
-  }
-  uintptr_t reservation_start = ComputeReservationStart(address, offset_ptr);
-  PA_DCHECK(*ReservationOffsetPointer(reservation_start) == 0);
-  return reservation_start;
-}
-#endif  // PA_BUILDFLAG(HAS_64_BIT_POINTERS)
-
-// Returns true if |address| is the beginning of the first super page of a
-// reservation, i.e. either a normal bucket super page, or the first super page
-// of direct map.
-// |address| must belong to an allocated super page.
-PA_ALWAYS_INLINE bool IsReservationStart(uintptr_t address) {
-  uint16_t* offset_ptr = ReservationOffsetPointer(address);
-  PA_DCHECK(*offset_ptr != kOffsetTagNotAllocated);
-  return ((*offset_ptr == kOffsetTagNormalBuckets) || (*offset_ptr == 0)) &&
-         (address % kSuperPageSize == 0);
-}
-
-// Returns true if |address| belongs to a normal bucket super page.
-PA_ALWAYS_INLINE bool IsManagedByNormalBuckets(uintptr_t address) {
-  uint16_t* offset_ptr = ReservationOffsetPointer(address);
-  return *offset_ptr == kOffsetTagNormalBuckets;
-}
-
-// Returns true if |address| belongs to a direct map region.
-PA_ALWAYS_INLINE bool IsManagedByDirectMap(uintptr_t address) {
-  uint16_t* offset_ptr = ReservationOffsetPointer(address);
-  return *offset_ptr != kOffsetTagNormalBuckets &&
-         *offset_ptr != kOffsetTagNotAllocated;
-}
-
-// Returns true if |address| belongs to a normal bucket super page or a direct
-// map region, i.e. belongs to an allocated super page.
-PA_ALWAYS_INLINE bool IsManagedByNormalBucketsOrDirectMap(uintptr_t address) {
-  uint16_t* offset_ptr = ReservationOffsetPointer(address);
-  return *offset_ptr != kOffsetTagNotAllocated;
-}
-
 }  // namespace partition_alloc::internal
 
 #endif  // PARTITION_ALLOC_RESERVATION_OFFSET_TABLE_H_
diff --git a/base/allocator/partition_allocator/src/partition_alloc/thread_isolation/thread_isolation.cc b/base/allocator/partition_allocator/src/partition_alloc/thread_isolation/thread_isolation.cc
index 8c2512b..f22b55b4 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/thread_isolation/thread_isolation.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/thread_isolation/thread_isolation.cc
@@ -75,11 +75,11 @@
       thread_isolation, *pool,
       offsetof(AddressPoolManager::Pool, alloc_bitset_));
 
-  uint16_t* pkey_reservation_offset_table =
-      GetReservationOffsetTable(kThreadIsolatedPoolHandle);
+  auto pkey_reservation_offset_table =
+      ReservationOffsetTable::Get(kThreadIsolatedPoolHandle);
   WriteProtectThreadIsolatedMemory(
-      thread_isolation, pkey_reservation_offset_table,
-      ReservationOffsetTable::kReservationOffsetTableLength);
+      thread_isolation, pkey_reservation_offset_table.GetData(),
+      ReservationOffsetTable::kThreadIsolatedOffsetTableLength);
 
 #if PA_BUILDFLAG(DCHECKS_ARE_ON)
   WriteProtectThreadIsolatedVariable(thread_isolation,
diff --git a/base/i18n/rtl.cc b/base/i18n/rtl.cc
index 60270ad..b37edc0 100644
--- a/base/i18n/rtl.cc
+++ b/base/i18n/rtl.cc
@@ -172,6 +172,15 @@
   DCHECK_EQ(rtl, IsRTL());
 }
 
+ScopedRTLForTesting::ScopedRTLForTesting(bool rtl) {
+  previous_rtl_state_ = IsRTL();
+  SetRTLForTesting(rtl);  // IN-TEST
+}
+
+ScopedRTLForTesting::~ScopedRTLForTesting() {
+  SetRTLForTesting(previous_rtl_state_);  // IN-TEST
+}
+
 bool ICUIsRTL() {
   if (g_icu_text_direction == UNKNOWN_DIRECTION) {
     const icu::Locale& locale = icu::Locale::getDefault();
diff --git a/base/i18n/rtl.h b/base/i18n/rtl.h
index 8bc0e545..545c58f 100644
--- a/base/i18n/rtl.h
+++ b/base/i18n/rtl.h
@@ -54,8 +54,25 @@
 BASE_I18N_EXPORT bool IsRTL();
 
 // A test utility function to set the application default text direction.
+// Prefer using ScopedRTLForTesting instead of this function directly.
 BASE_I18N_EXPORT void SetRTLForTesting(bool rtl);
 
+// A RAII wrapper for setting RTL in tests. Automatically restores the previous
+// RTL state when destroyed. This is the preferred way to set RTL state in
+// tests.
+class BASE_I18N_EXPORT ScopedRTLForTesting {
+ public:
+  explicit ScopedRTLForTesting(bool rtl);
+  ~ScopedRTLForTesting();
+
+  // Not copyable or movable
+  ScopedRTLForTesting(const ScopedRTLForTesting&) = delete;
+  ScopedRTLForTesting& operator=(const ScopedRTLForTesting&) = delete;
+
+ private:
+  bool previous_rtl_state_;
+};
+
 // Returns whether the text direction for the default ICU locale is RTL.  This
 // assumes that SetICUDefaultLocale has been called to set the default locale to
 // the UI locale of Chrome.
diff --git a/base/i18n/rtl_unittest.cc b/base/i18n/rtl_unittest.cc
index 5930b8822..2cbf1ba 100644
--- a/base/i18n/rtl_unittest.cc
+++ b/base/i18n/rtl_unittest.cc
@@ -326,7 +326,7 @@
   test::ScopedRestoreICUDefaultLocale restore_locale;
   for (size_t i = 0; i < 2; ++i) {
     // Toggle the application default text direction (to try each direction).
-    SetRTLForTesting(!IsRTL());
+    ScopedRTLForTesting scoped_rtl(!IsRTL());
 
     std::u16string empty;
     WrapStringWithLTRFormatting(&empty);
@@ -378,7 +378,7 @@
   test::ScopedRestoreICUDefaultLocale restore_locale;
   for (size_t i = 0; i < 2; ++i) {
     // Toggle the application default text direction (to try each direction).
-    SetRTLForTesting(!IsRTL());
+    ScopedRTLForTesting scoped_rtl(!IsRTL());
     for (auto& test_case : cases) {
       std::u16string input = WideToUTF16(test_case.path);
       std::u16string output = GetDisplayStringInLTRDirectionality(input);
@@ -466,7 +466,7 @@
   test::ScopedRestoreICUDefaultLocale restore_locale;
   for (size_t i = 0; i < 2; ++i) {
     // Toggle the application default text direction (to try each direction).
-    SetRTLForTesting(!IsRTL());
+    ScopedRTLForTesting scoped_rtl(!IsRTL());
 
     for (auto*& test_case : cases) {
       std::u16string unadjusted_string = WideToUTF16(test_case);
@@ -519,7 +519,7 @@
   test::ScopedRestoreICUDefaultLocale restore_locale;
   for (size_t i = 0; i < 2; ++i) {
     // Toggle the application default text direction (to try each direction).
-    SetRTLForTesting(!IsRTL());
+    ScopedRTLForTesting scoped_rtl(!IsRTL());
     for (auto& test_case : cases) {
       std::u16string unsanitized_text = WideToUTF16(test_case.unformated_text);
       std::u16string sanitized_text = WideToUTF16(test_case.formatted_text);
diff --git a/base/process/launch.h b/base/process/launch.h
index 84e9ff2..8f08514 100644
--- a/base/process/launch.h
+++ b/base/process/launch.h
@@ -472,6 +472,17 @@
 //   successive calls to effectively produce the full `output` from the `cl`
 //   process.
 // * an optional `final_status` `TerminationStatus` value on function return.
+//
+// Returns `true` if the application runs and exits. If this is the case the
+// exit code of the application is available in `*exit_code`, and `final_status`
+// will be `TERMINATION_STATUS_NORMAL_TERMINATION`.
+//
+// Returns `false` under the following conditions:
+// * If the application does not exist, `final_status` will be
+// `TERMINATION_STATUS_LAUNCH_FAILED`.
+// * If the application does not terminate within `timeout`, `final_status` will
+// be `TERMINATION_STATUS_STILL_RUNNING`.
+//
 // Note that the expected use cases for this function do not expect `cl` to
 // produce a lot of output. This function will not work optimally with lots of
 // output from the `cl` process, since it waits a second each time between
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc
index baf5975a..9b03f31 100644
--- a/base/process/launch_win.cc
+++ b/base/process/launch_win.cc
@@ -99,8 +99,8 @@
 
   const ElapsedTimer timer;
 
+  bool process_exited = false;
   do {
-    bool process_exited = false;
     {
       // It is okay to allow this process to wait on the launched process as a
       // process launched with GetAppOutput*() shouldn't wait back on the
@@ -108,7 +108,7 @@
       internal::GetAppOutputScopedAllowBaseSyncPrimitives allow_wait;
       ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                               BlockingType::MAY_BLOCK);
-      process_exited = process.WaitForExitWithTimeout(Seconds(1), nullptr);
+      process_exited = process.WaitForExitWithTimeout(Seconds(1), exit_code);
     }
 
     // Read output from the child process's pipe for STDOUT
@@ -150,13 +150,11 @@
     still_waiting({});
   } while (timer.Elapsed() < timeout);
 
-  TerminationStatus status = GetTerminationStatus(process.Handle(), exit_code);
   if (final_status) {
-    *final_status = status;
+    *final_status = process_exited ? TERMINATION_STATUS_NORMAL_TERMINATION
+                                   : TERMINATION_STATUS_STILL_RUNNING;
   }
-  return status != TERMINATION_STATUS_PROCESS_CRASHED &&
-         status != TERMINATION_STATUS_STILL_RUNNING &&
-         status != TERMINATION_STATUS_ABNORMAL_TERMINATION;
+  return process_exited;
 }
 
 Process LaunchElevatedProcess(const CommandLine& cmdline,
@@ -490,7 +488,8 @@
 bool GetAppOutputAndError(const CommandLine& cl, std::string* output) {
   int exit_code;
   return GetAppOutputInternal(cl.GetCommandLineString(), true, output,
-                              &exit_code);
+                              &exit_code) &&
+         !exit_code;
 }
 
 bool GetAppOutputWithExitCode(const CommandLine& cl,
@@ -515,7 +514,7 @@
 
 bool GetAppOutput(CommandLine::StringViewType cl, std::string* output) {
   int exit_code;
-  return GetAppOutputInternal(cl, false, output, &exit_code);
+  return GetAppOutputInternal(cl, false, output, &exit_code) && !exit_code;
 }
 
 void RaiseProcessToHighPriority() {
diff --git a/base/process/process_util_unittest.cc b/base/process/process_util_unittest.cc
index 2b3435b..dfc317597 100644
--- a/base/process/process_util_unittest.cc
+++ b/base/process/process_util_unittest.cc
@@ -945,13 +945,7 @@
   command.AppendArg("-x");
   command.AppendArg(NumberToString(kExpectedExitCode));
   command.AppendArg(kEchoMessage2);
-#if BUILDFLAG(IS_WIN)
-  // On Windows, anything that quits with a nonzero status code is handled as a
-  // "crash", so just ignore GetAppOutputWithExitCode's return value.
-  GetAppOutputWithExitCode(command, &output, &exit_code);
-#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
   EXPECT_TRUE(GetAppOutputWithExitCode(command, &output, &exit_code));
-#endif
   EXPECT_EQ(kEchoMessage2, output);
   EXPECT_EQ(kExpectedExitCode, exit_code);
 }
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index 382e080..1987d3cf 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -11,7 +11,6 @@
 #include <functional>
 #include <memory>
 #include <optional>
-#include <queue>
 #include <set>
 #include <utility>
 #include <vector>
diff --git a/base/task/thread_pool/task_tracker.h b/base/task/thread_pool/task_tracker.h
index 50b62529..4b1fd1b6 100644
--- a/base/task/thread_pool/task_tracker.h
+++ b/base/task/thread_pool/task_tracker.h
@@ -10,7 +10,6 @@
 #include <limits>
 #include <memory>
 #include <optional>
-#include <queue>
 #include <string>
 
 #include "base/atomic_sequence_num.h"
diff --git a/build/linux/amd64/module.modulemap b/build/linux/amd64/module.modulemap
new file mode 100644
index 0000000..4c45479
--- /dev/null
+++ b/build/linux/amd64/module.modulemap
@@ -0,0 +1,29 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module sysroot_features [system] [extern_c] {
+  header "../debian_bullseye_amd64-sysroot/usr/include/features.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/stdc-predef.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/sys/cdefs.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/gnu/stubs.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/bits/wordsize.h"
+}
+
+module sysroot_bits [system] [extern_c] {
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/bits/floatn.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/bits/stdint-intn.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/bits/stdint-uintn.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h"
+}
+
+module sysroot_types [system] [extern_c] {
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/sys/types.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h"
+  header "../debian_bullseye_amd64-sysroot/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h"
+  // sys/types.h imports types with a #include that are designed to make be made available to users.
+  // Eg. if you include sys/types.h, it should give you size_t, but size_t is defined by a transitive dependency.
+  export *
+}
\ No newline at end of file
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index 2b3921cc..2ed9244 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -7,7 +7,6 @@
 
 #include <map>
 #include <memory>
-#include <queue>
 #include <vector>
 
 #include "base/containers/circular_deque.h"
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 86007c8..f4727068 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -994,15 +994,13 @@
   }
 
   # Add the ANGLE .dylibs in the MODULE_DIR of Chromium.app
-  if (!use_static_angle) {
-    bundle_data("angle_binaries") {
-      sources = [
-        "$root_out_dir/egl_intermediates/libEGL.dylib",
-        "$root_out_dir/egl_intermediates/libGLESv2.dylib",
-      ]
-      outputs = [ "{{bundle_contents_dir}}/Libraries/{{source_file_part}}" ]
-      public_deps = [ "//ui/gl:angle_library_copy" ]
-    }
+  bundle_data("angle_binaries") {
+    sources = [
+      "$root_out_dir/egl_intermediates/libEGL.dylib",
+      "$root_out_dir/egl_intermediates/libGLESv2.dylib",
+    ]
+    outputs = [ "{{bundle_contents_dir}}/Libraries/{{source_file_part}}" ]
+    public_deps = [ "//ui/gl:angle_library_copy" ]
   }
 
   # Add the SwiftShader .dylibs in the MODULE_DIR of Chromium.app
@@ -1201,6 +1199,7 @@
     public_deps = [ ":chrome_dll" ]
 
     bundle_deps = [
+      ":angle_binaries",
       ":chrome_framework_helpers",
       ":chrome_framework_plugins",
       ":chrome_framework_resources",
@@ -1216,10 +1215,6 @@
       bundle_deps += [ ":preinstalled_apps" ]
     }
 
-    if (!use_static_angle) {
-      bundle_deps += [ ":angle_binaries" ]
-    }
-
     configs += [ ":chrome_dll_symbol_order" ]
     if (!is_component_build && !using_sanitizer) {
       configs += [ ":chrome_dll_symbol_exports" ]
@@ -1301,6 +1296,8 @@
     _chrome_symbols_sources = [
       "$root_out_dir/$chrome_product_full_name.app/Contents/MacOS/$chrome_product_full_name",
       "$root_out_dir/chrome_crashpad_handler",
+      "$root_out_dir/libEGL.dylib",
+      "$root_out_dir/libGLESv2.dylib",
       "$root_out_dir/libvk_swiftshader.dylib",
       _framework_binary_path,
     ]
@@ -1308,12 +1305,6 @@
       _chrome_symbols_sources +=
           [ "$root_out_dir/liboptimization_guide_internal.dylib" ]
     }
-    if (!use_static_angle) {
-      _chrome_symbols_sources += [
-        "$root_out_dir/libEGL.dylib",
-        "$root_out_dir/libGLESv2.dylib",
-      ]
-    }
 
     foreach(helper_params, chrome_mac_helpers) {
       _chrome_symbols_sources += [ "$root_out_dir/${chrome_helper_name}${helper_params[2]}.app/Contents/MacOS/${chrome_helper_name}${helper_params[2]}" ]
@@ -1346,18 +1337,14 @@
         ":chrome_app",
         ":chrome_framework",
         "//components/crash/core/app:chrome_crashpad_handler",
+        "//third_party/angle:libEGL",
+        "//third_party/angle:libGLESv2",
         "//third_party/swiftshader/src/Vulkan:swiftshader_libvulkan",
         dump_syms,
       ]
       if (build_with_internal_optimization_guide) {
         deps += [ "//components/optimization_guide/internal:optimization_guide_internal" ]
       }
-      if (!use_static_angle) {
-        deps += [
-          "//third_party/angle:libEGL",
-          "//third_party/angle:libGLESv2",
-        ]
-      }
 
       foreach(helper_params, chrome_mac_helpers) {
         deps += [ ":chrome_helper_app_${helper_params[0]}" ]
@@ -1375,33 +1362,25 @@
         "$root_out_dir/$chrome_framework_name.dSYM",
         "$root_out_dir/$chrome_product_full_name.dSYM",
         "$root_out_dir/chrome_crashpad_handler.dSYM",
+        "$root_out_dir/libEGL.dylib.dSYM",
+        "$root_out_dir/libGLESv2.dylib.dSYM",
         "$root_out_dir/libvk_swiftshader.dylib.dSYM",
       ]
       if (build_with_internal_optimization_guide) {
         _dsyms += [ "$root_out_dir/liboptimization_guide_internal.dylib.dSYM" ]
       }
-      if (!use_static_angle) {
-        _dsyms += [
-          "$root_out_dir/libEGL.dylib.dSYM",
-          "$root_out_dir/libGLESv2.dylib.dSYM",
-        ]
-      }
 
       deps = [
         ":chrome_app",
         ":chrome_framework",
         "//components/crash/core/app:chrome_crashpad_handler",
+        "//third_party/angle:libEGL",
+        "//third_party/angle:libGLESv2",
         "//third_party/swiftshader/src/Vulkan:swiftshader_libvulkan",
       ]
       if (build_with_internal_optimization_guide) {
         deps += [ "//components/optimization_guide/internal:optimization_guide_internal" ]
       }
-      if (!use_static_angle) {
-        deps += [
-          "//third_party/angle:libEGL",
-          "//third_party/angle:libGLESv2",
-        ]
-      }
 
       foreach(helper_params, chrome_mac_helpers) {
         _dsyms +=
@@ -1711,6 +1690,8 @@
   if (!(is_debug && use_debug_fission)) {
     group("linux_symbols") {
       deps = [
+        ":angle_egl_symbols",
+        ":angle_gles_symbols",
         ":chrome_crashpad_symbols",
         ":chrome_symbols",
       ]
@@ -1723,12 +1704,6 @@
       if (build_with_internal_optimization_guide) {
         deps += [ ":optimization_guide_symbols" ]
       }
-      if (!use_static_angle) {
-        deps += [
-          ":angle_egl_symbols",
-          ":angle_gles_symbols",
-        ]
-      }
     }
     extract_symbols("chrome_symbols") {
       binary = "$root_out_dir/chrome"
@@ -1766,31 +1741,29 @@
 
       deps = [ "//third_party/swiftshader/src/Vulkan:swiftshader_libvulkan" ]
     }
-    if (!use_static_angle) {
-      extract_symbols("angle_egl_symbols") {
-        binary = "$root_out_dir/libEGL.so"
+    extract_symbols("angle_egl_symbols") {
+      binary = "$root_out_dir/libEGL.so"
 
-        if (current_cpu == "x86") {
-          # GYP used "ia32" so keep that naming for back-compat.
-          symbol_file = "$root_out_dir/angle_libegl.breakpad.ia32"
-        } else {
-          symbol_file = "$root_out_dir/angle_libegl.breakpad.$current_cpu"
-        }
-
-        deps = [ "//third_party/angle:libEGL" ]
+      if (current_cpu == "x86") {
+        # GYP used "ia32" so keep that naming for back-compat.
+        symbol_file = "$root_out_dir/angle_libegl.breakpad.ia32"
+      } else {
+        symbol_file = "$root_out_dir/angle_libegl.breakpad.$current_cpu"
       }
-      extract_symbols("angle_gles_symbols") {
-        binary = "$root_out_dir/libGLESv2.so"
 
-        if (current_cpu == "x86") {
-          # GYP used "ia32" so keep that naming for back-compat.
-          symbol_file = "$root_out_dir/angle_libgles.breakpad.ia32"
-        } else {
-          symbol_file = "$root_out_dir/angle_libgles.breakpad.$current_cpu"
-        }
+      deps = [ "//third_party/angle:libEGL" ]
+    }
+    extract_symbols("angle_gles_symbols") {
+      binary = "$root_out_dir/libGLESv2.so"
 
-        deps = [ "//third_party/angle:libGLESv2" ]
+      if (current_cpu == "x86") {
+        # GYP used "ia32" so keep that naming for back-compat.
+        symbol_file = "$root_out_dir/angle_libgles.breakpad.ia32"
+      } else {
+        symbol_file = "$root_out_dir/angle_libgles.breakpad.$current_cpu"
       }
+
+      deps = [ "//third_party/angle:libGLESv2" ]
     }
     if (!is_chromeos && angle_shared_libvulkan) {
       extract_symbols("angle_libvulkan_symbols") {
diff --git a/chrome/VERSION b/chrome/VERSION
index ee877c9..0ec1be0 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=138
 MINOR=0
-BUILD=7192
+BUILD=7193
 PATCH=0
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 44e2ff56..ce56834 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -163,6 +163,7 @@
     "//chrome/test/android:chrome_java_test_support_common",
     "//components/browser_ui/styles/android:java",
     "//components/browser_ui/styles/android:java_resources",
+    "//components/browser_ui/util/android:java",
     "//components/browser_ui/widget/android:java",
     "//components/collaboration/public:core_java",
     "//components/commerce/core:proto_java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
index e17f81ad..c3905448 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
@@ -18,7 +18,6 @@
 import android.text.style.ForegroundColorSpan;
 import android.util.Size;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -74,6 +73,7 @@
 import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
 import org.chromium.components.browser_ui.edge_to_edge.EdgeToEdgePadAdjuster;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.ActionConfirmationDialog;
 import org.chromium.components.browser_ui.widget.ActionConfirmationDialog.DialogDismissType;
 import org.chromium.components.browser_ui.widget.FadingShadow;
@@ -214,7 +214,7 @@
                     return new TabActionListener() {
                         @Override
                         public void run(
-                                View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
+                                View view, int tabId, @Nullable MotionEventInfo triggeringMotion) {
                             // Intentional no-op.
                         }
 
@@ -222,7 +222,7 @@
                         public void run(
                                 View view,
                                 String syncId,
-                                @Nullable MotionEvent triggeringMotionEvent) {
+                                @Nullable MotionEventInfo triggeringMotion) {
                             SavedTabGroup savedTabGroup = mTabGroupSyncService.getGroup(syncId);
                             mIsOpeningLastItem =
                                     getArchivedTabCount() == savedTabGroup.savedTabs.size();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinatorUnitTest.java
index 37c992c..7ea1b6c7f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContextMenuCoordinatorUnitTest.java
@@ -54,9 +54,9 @@
 import org.chromium.chrome.browser.tabmodel.TabRemover;
 import org.chromium.chrome.browser.tasks.tab_management.TabGridContextMenuCoordinator.ShowTabListEditor;
 import org.chromium.chrome.browser.tasks.tab_management.TabListEditorCoordinator.TabListEditorController;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.list_view.FakeListViewTouchTracker;
 import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker;
-import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker.ListViewTouchInfo;
 import org.chromium.components.collaboration.CollaborationService;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
 import org.chromium.ui.base.TestActivity;
@@ -301,7 +301,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createTouchMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -317,7 +317,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createMouseMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
index f4600e7..e494cb14 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
@@ -272,7 +272,7 @@
             mTabClosedListener.run(
                     viewHolder.itemView,
                     simpleViewHolder.model.get(TabProperties.TAB_ID),
-                    /* triggeringMotionEvent= */ null);
+                    /* triggeringMotion= */ null);
 
             RecordUserAction.record("MobileStackViewSwipeCloseTab." + mComponentName);
         } else if (simpleViewHolder.model.get(CARD_TYPE) == MESSAGE) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index b5d433e..7f558c5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -11,7 +11,6 @@
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.util.Size;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
@@ -41,6 +40,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.TabActionState;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.util.OnPeripheralClickListener;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.widget.ChromeImageView;
@@ -294,7 +294,7 @@
             view.setOnClickListener(
                     v ->
                             runTabActionListener(
-                                    listener, v, propertyModel, /* triggeringMotionEvent= */ null));
+                                    listener, v, propertyModel, /* triggeringMotion= */ null));
         }
     }
 
@@ -322,12 +322,9 @@
         view.setOnTouchListener(
                 new OnPeripheralClickListener(
                         view,
-                        triggeringMotionEvent ->
+                        triggeringMotion ->
                                 runTabActionListener(
-                                        tabActionListener,
-                                        view,
-                                        propertyModel,
-                                        triggeringMotionEvent)));
+                                        tabActionListener, view, propertyModel, triggeringMotion)));
     }
 
     static void setNullableLongClickListener(
@@ -340,7 +337,7 @@
             view.setOnLongClickListener(
                     v -> {
                         runTabActionListener(
-                                listener, v, propertyModel, /* triggeringMotionEvent= */ null);
+                                listener, v, propertyModel, /* triggeringMotion= */ null);
                         return true;
                     });
         }
@@ -350,15 +347,12 @@
             @NonNull TabActionListener tabActionListener,
             @NonNull View view,
             @NonNull PropertyModel propertyModel,
-            @Nullable MotionEvent triggeringMotionEvent) {
+            @Nullable MotionEventInfo triggeringMotion) {
         if (propertyModel.containsKey(TabProperties.TAB_GROUP_SYNC_ID)) {
             tabActionListener.run(
-                    view,
-                    propertyModel.get(TabProperties.TAB_GROUP_SYNC_ID),
-                    triggeringMotionEvent);
+                    view, propertyModel.get(TabProperties.TAB_GROUP_SYNC_ID), triggeringMotion);
         } else {
-            tabActionListener.run(
-                    view, propertyModel.get(TabProperties.TAB_ID), triggeringMotionEvent);
+            tabActionListener.run(view, propertyModel.get(TabProperties.TAB_ID), triggeringMotion);
         }
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java
index 1291fbb9..67eef997 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java
@@ -7,7 +7,6 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Resources;
-import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.StringRes;
@@ -20,6 +19,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.BrowserUiListMenuUtils;
 import org.chromium.components.collaboration.CollaborationService;
 import org.chromium.components.data_sharing.member_role.MemberRole;
@@ -68,7 +68,7 @@
     TabListMediator.TabActionListener getTabActionListener() {
         return new TabListMediator.TabActionListener() {
             @Override
-            public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
+            public void run(View view, int tabId, @Nullable MotionEventInfo triggeringMotion) {
                 @Nullable TabModel tabModel = getTabModel();
                 if (tabModel == null) return;
 
@@ -88,7 +88,7 @@
             }
 
             @Override
-            public void run(View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
+            public void run(View view, String syncId, @Nullable MotionEventInfo triggeringMotion) {
                 // Intentional no-op.
             }
         };
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java
index b4fcd8e..6dadc15 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java
@@ -246,9 +246,7 @@
         when(mCollaborationService.getCurrentUserRoleForGroup(COLLABORATION_ID1))
                 .thenReturn(MemberRole.MEMBER);
 
-        mMenuCoordinator
-                .getTabActionListener()
-                .run(mView, TAB_ID, /* triggeringMotionEvent= */ null);
+        mMenuCoordinator.getTabActionListener().run(mView, TAB_ID, /* triggeringMotion= */ null);
 
         verify(mMenuCoordinator).buildMenuActionItems(any(), eq(TAB_GROUP_TOKEN));
         verify(mMenuCoordinator)
@@ -271,9 +269,7 @@
         when(mCollaborationService.getCurrentUserRoleForGroup(COLLABORATION_ID1))
                 .thenReturn(MemberRole.OWNER);
 
-        mMenuCoordinator
-                .getTabActionListener()
-                .run(mView, TAB_ID, /* triggeringMotionEvent= */ null);
+        mMenuCoordinator.getTabActionListener().run(mView, TAB_ID, /* triggeringMotion= */ null);
 
         verify(mMenuCoordinator).buildMenuActionItems(any(), eq(TAB_GROUP_TOKEN));
         verify(mMenuCoordinator)
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 0adc19c..536969b3 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
@@ -94,6 +94,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabListEditorActionMetricGroups;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 import org.chromium.components.collaboration.CollaborationService;
@@ -195,20 +196,20 @@
          *
          * @param view {@link View} for the tab.
          * @param tabId ID of the tab.
-         * @param triggeringMotionEvent {@link MotionEvent} that triggered the action; it will be
+         * @param triggeringMotion {@link MotionEventInfo} that triggered the action; it will be
          *     {@code null} if the action is not a result of direct interpretation of {@link
          *     MotionEvent}s. For example, this parameter will be {@code null} if the action is run
          *     by a {@link android.view.View.OnClickListener} where {@link MotionEvent} is not
          *     available.
          */
-        void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent);
+        void run(View view, int tabId, @Nullable MotionEventInfo triggeringMotion);
 
         /**
          * Runs the action for the given {@code view} and tab group {@code syncId}.
          *
-         * @see #run(View, int, MotionEvent)
+         * @see #run(View, int, MotionEventInfo)
          */
-        void run(View view, String syncId, @Nullable MotionEvent triggeringMotionEvent);
+        void run(View view, String syncId, @Nullable MotionEventInfo triggeringMotion);
     }
 
     /**
@@ -408,7 +409,7 @@
     private final TabActionListener mTabSelectedListener =
             new TabActionListener() {
                 @Override
-                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
+                public void run(View view, int tabId, @Nullable MotionEventInfo triggeringMotion) {
                     if (mModelList.indexFromTabId(tabId) == TabModel.INVALID_TAB_INDEX) return;
 
                     mNextTabId = tabId;
@@ -446,7 +447,7 @@
 
                 @Override
                 public void run(
-                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
+                        View view, String syncId, @Nullable MotionEventInfo triggeringMotion) {
                     // Intentional no-op.
                 }
 
@@ -479,7 +480,7 @@
     private final TabActionListener mSelectableTabOnClickListener =
             new TabActionListener() {
                 @Override
-                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
+                public void run(View view, int tabId, @Nullable MotionEventInfo triggeringMotion) {
                     SelectionDelegate<TabListEditorItemSelectionId> selectionDelegate =
                             getTabSelectionDelegate();
                     assert selectionDelegate != null;
@@ -508,7 +509,7 @@
 
                 @Override
                 public void run(
-                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
+                        View view, String syncId, @Nullable MotionEventInfo triggeringMotion) {
                     SelectionDelegate<TabListEditorItemSelectionId> selectionDelegate =
                             getTabSelectionDelegate();
                     assert selectionDelegate != null;
@@ -1169,7 +1170,7 @@
                 new TabActionListener() {
                     @Override
                     public void run(
-                            View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
+                            View view, int tabId, @Nullable MotionEventInfo triggeringMotion) {
                         // TODO(crbug.com/40638921): Consider disabling all touch events during
                         // animation.
                         if (mModelList.indexFromTabId(tabId) == TabModel.INVALID_TAB_INDEX) return;
@@ -1183,7 +1184,7 @@
                         if (mActionsOnAllRelatedTabs && filter.isTabInTabGroup(closingTab)) {
                             onGroupClosedFrom(tabId);
 
-                            // TODO(crbug.com/375468032): use "triggeringMotionEvent" to determine
+                            // TODO(crbug.com/375468032): use "triggeringMotion" to determine
                             //  if the "undo" snackbar should be shown when closing a tab group.
                             TabUiUtils.closeTabGroup(
                                     mCurrentTabGroupModelFilterSupplier.get(),
@@ -1197,8 +1198,7 @@
                         onTabClosedFrom(tabId, mComponentName);
                         Tab currentTab = TabModelUtils.getCurrentTab(tabModel);
                         Tab nextTab = currentTab == closingTab ? getNextTab(tabId) : null;
-                        boolean allowUndo =
-                                TabClosureParamsUtils.shouldAllowUndo(triggeringMotionEvent);
+                        boolean allowUndo = TabClosureParamsUtils.shouldAllowUndo(triggeringMotion);
                         TabClosureParams closureParams =
                                 TabClosureParams.closeTab(closingTab)
                                         .recommendedNextTab(nextTab)
@@ -1215,7 +1215,7 @@
 
                     @Override
                     public void run(
-                            View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
+                            View view, String syncId, @Nullable MotionEventInfo triggeringMotion) {
                         int index = mModelList.indexFromSyncId(syncId);
                         if (index == TabModel.INVALID_TAB_INDEX) return;
 
@@ -1268,7 +1268,7 @@
                 new TabActionListener() {
                     @Override
                     public void run(
-                            View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
+                            View view, int tabId, @Nullable MotionEventInfo triggeringMotion) {
                         // The DefaultItemAnimator is prone to crashing in combination with the
                         // swipe animation when closing the last tab. Avoid this issue by disabling
                         // the default item animation for the duration of the removal of the last
@@ -1284,7 +1284,7 @@
                             mRecyclerViewItemAnimationToggle.setDisableItemAnimations(true);
                         }
 
-                        mTabClosedListener.run(view, tabId, /* triggeringMotionEvent= */ null);
+                        mTabClosedListener.run(view, tabId, /* triggeringMotion= */ null);
 
                         // It is necessary to post the restoration as otherwise any animation
                         // triggered by removing the tab will still use the animator as they are
@@ -1301,7 +1301,7 @@
 
                     @Override
                     public void run(
-                            View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
+                            View view, String syncId, @Nullable MotionEventInfo triggeringMotion) {
                         // Swipe is disabled in the {@link ArchivedTabsDialogCoordinator}
                         // implementation of the TabListMediator. Intentional no-op.
                     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
index 2173dfa..f126d29 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
@@ -66,7 +66,7 @@
                             data.tabActionListener.run(
                                     v,
                                     model.get(TabProperties.TAB_ID),
-                                    /* triggeringMotionEvent= */ null);
+                                    /* triggeringMotion= */ null);
                         });
             } else {
                 button.setOnClickListener(
@@ -75,7 +75,7 @@
                                     .run(
                                             v,
                                             model.get(TabProperties.TAB_ID),
-                                            /* triggeringMotionEvent= */ null);
+                                            /* triggeringMotion= */ null);
                         });
             }
             setContentDescription(view, model);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java
index 91e0eac..160c368 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java
@@ -8,7 +8,6 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.FOCUS_TAB_INDEX_FOR_ACCESSIBILITY;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.INITIAL_SCROLL_INDEX;
 
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -33,6 +32,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabListEditorCoordinator.TabListEditorController;
 import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.GridCardOnClickListenerProvider;
 import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.TabActionListener;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -51,14 +51,14 @@
     private final TabActionListener mTabGridDialogOpener =
             new TabActionListener() {
                 @Override
-                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
+                public void run(View view, int tabId, @Nullable MotionEventInfo triggeringMotion) {
                     openTabGroupDialog(tabId);
                     RecordUserAction.record("TabGridDialog.ExpandedFromSwitcher");
                 }
 
                 @Override
                 public void run(
-                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
+                        View view, String syncId, @Nullable MotionEventInfo triggeringMotion) {
                     // Intentional no-op.
                 }
             };
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediatorUnitTest.java
index 07414e9..cc10a74 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediatorUnitTest.java
@@ -352,7 +352,7 @@
     public void testOpenTabGridDialog() {
         TabActionListener listener = mMediator.openTabGridDialog(mGroupedTab1);
         assertNotNull(listener);
-        listener.run(mCustomView, mGroupedTab1.getId(), /* triggeringMotionEvent= */ null);
+        listener.run(mCustomView, mGroupedTab1.getId(), /* triggeringMotion= */ null);
 
         verify(mTabGridDialogController).resetWithListOfTabs(List.of(mGroupedTab1, mGroupedTab2));
     }
@@ -368,7 +368,7 @@
 
         TabActionListener listener = mMediator.openTabGridDialog(mUngroupedTab);
         assertNotNull(listener);
-        listener.run(mCustomView, mUngroupedTab.getId(), /* triggeringMotionEvent= */ null);
+        listener.run(mCustomView, mUngroupedTab.getId(), /* triggeringMotion= */ null);
         verify(mTabGridDialogController).resetWithListOfTabs(List.of(mUngroupedTab));
     }
 
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
index 2c36cc5..c452605 100644
--- a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
@@ -767,6 +767,11 @@
       <message name="IDS_PLUS_HIDDEN_TAB_COUNT" desc="A string to show how many other tabs are contained in a tab group who's favicons are not shown. [CHAR_LIMIT=5]">
         +<ph name="HIDDEN_TABS_COUNT">%1$s<ex>3</ex></ph>
       </message>
+      <message name="IDS_MOVE_GROUP_TO_ANOTHER_WINDOW_CONTEXT_MENU_ITEM" desc="Menu item text which will be displayed for tab group context menu on strip to move the group to another window.">
+        {WINDOW_COUNT, plural,
+          =1 {Move group to new window}
+          other {Move group to another window}}
+      </message>
       <message name="IDS_OPEN_NEW_TAB_IN_GROUP_CONTEXT_MENU_ITEM" desc="Menu item text which will be display for tab group context menu on strip to allow opening a new tab in the tab group.">
         New tab in group
       </message>
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_MOVE_GROUP_TO_ANOTHER_WINDOW_CONTEXT_MENU_ITEM.png.sha1 b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_MOVE_GROUP_TO_ANOTHER_WINDOW_CONTEXT_MENU_ITEM.png.sha1
new file mode 100644
index 0000000..4f9c3e9
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_MOVE_GROUP_TO_ANOTHER_WINDOW_CONTEXT_MENU_ITEM.png.sha1
@@ -0,0 +1 @@
+271aa55f95f358c956086451c55c170f7fdd92bd
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsDialogTest.java
index 3bad6b6..db6deac 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/CloseAllTabsDialogTest.java
@@ -50,6 +50,7 @@
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.R;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.ui.base.DeviceFormFactor;
 
 import java.util.Arrays;
@@ -177,8 +178,10 @@
                                         : null));
     }
 
-    /** Creates a {@link MotionEvent} that matches one from a mouse and would trigger a click. */
-    private static MotionEvent createClickTriggeringMotionFromMouse() {
+    /**
+     * Creates a {@link MotionEventInfo} that matches one from a mouse and would trigger a click.
+     */
+    private static MotionEventInfo createClickTriggeringMotionFromMouse() {
         long downTime = SystemClock.uptimeMillis();
 
         PointerProperties pointerProperties = new MotionEvent.PointerProperties();
@@ -189,21 +192,22 @@
         pointerCoords.x = 0;
         pointerCoords.y = 0;
 
-        return MotionEvent.obtain(
-                downTime,
-                downTime + 50,
-                MotionEvent.ACTION_UP,
-                /* pointerCount= */ 1,
-                new PointerProperties[] {pointerProperties},
-                new PointerCoords[] {pointerCoords},
-                /* metaState= */ 0,
-                /* buttonState= */ 0,
-                /* xPrecision= */ 1.0f,
-                /* yPrecision= */ 1.0f,
-                /* deviceId= */ 0,
-                /* edgeFlags= */ 0,
-                InputDevice.SOURCE_MOUSE,
-                /* flags= */ 0);
+        return MotionEventInfo.fromMotionEvent(
+                MotionEvent.obtain(
+                        downTime,
+                        downTime + 50,
+                        MotionEvent.ACTION_UP,
+                        /* pointerCount= */ 1,
+                        new PointerProperties[] {pointerProperties},
+                        new PointerCoords[] {pointerCoords},
+                        /* metaState= */ 0,
+                        /* buttonState= */ 0,
+                        /* xPrecision= */ 1.0f,
+                        /* yPrecision= */ 1.0f,
+                        /* deviceId= */ 0,
+                        /* edgeFlags= */ 0,
+                        InputDevice.SOURCE_MOUSE,
+                        /* flags= */ 0));
     }
 
     /**
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 d7d2aa98..1ad20987 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
@@ -95,6 +95,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.TabActionState;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 import org.chromium.components.commerce.PriceTracking.BuyableProduct;
 import org.chromium.components.commerce.PriceTracking.PriceTrackingData;
@@ -214,14 +215,14 @@
     private final TabListMediator.TabActionListener mMockCloseListener =
             new TabListMediator.TabActionListener() {
                 @Override
-                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
+                public void run(View view, int tabId, @Nullable MotionEventInfo triggeringMotion) {
                     mCloseClicked.set(true);
                     mCloseTabId.set(tabId);
                 }
 
                 @Override
                 public void run(
-                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {}
+                        View view, String syncId, @Nullable MotionEventInfo triggeringMotion) {}
             };
     private final AtomicBoolean mCloseClicked = new AtomicBoolean();
     private final AtomicInteger mCloseTabId = new AtomicInteger();
@@ -229,14 +230,14 @@
     private final TabListMediator.TabActionListener mMockSelectedListener =
             new TabListMediator.TabActionListener() {
                 @Override
-                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
+                public void run(View view, int tabId, @Nullable MotionEventInfo triggeringMotion) {
                     mSelectClicked.set(true);
                     mSelectTabId.set(tabId);
                 }
 
                 @Override
                 public void run(
-                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {}
+                        View view, String syncId, @Nullable MotionEventInfo triggeringMotion) {}
             };
     private final AtomicBoolean mSelectClicked = new AtomicBoolean();
     private final AtomicInteger mSelectTabId = new AtomicInteger();
@@ -244,14 +245,14 @@
     private final TabListMediator.TabActionListener mMockCreateGroupButtonListener =
             new TabListMediator.TabActionListener() {
                 @Override
-                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
+                public void run(View view, int tabId, @Nullable MotionEventInfo triggeringMotion) {
                     mCreateGroupButtonClicked.set(true);
                     mCreateGroupTabId.set(tabId);
                 }
 
                 @Override
                 public void run(
-                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {}
+                        View view, String syncId, @Nullable MotionEventInfo triggeringMotion) {}
             };
     private final AtomicBoolean mCreateGroupButtonClicked = new AtomicBoolean();
     private final AtomicInteger mCreateGroupTabId = new AtomicInteger();
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
index d80fc8349..2674696 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
@@ -355,7 +355,7 @@
         GridCardOnClickListenerProvider provider =
                 mCoordinator.getGridCardOnClickListenerProviderForTesting();
         TabActionListener listener = provider.openTabGridDialog(TAB_GROUP_ID_STRING);
-        listener.run(mItemView1, TAB_GROUP_ID_STRING, /* triggeringMotionEvent= */ null);
+        listener.run(mItemView1, TAB_GROUP_ID_STRING, /* triggeringMotion= */ null);
 
         verify(mTabGroupUiActionHandler).openTabGroup(TAB_GROUP_ID_STRING);
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
index 4c7bd82..2a6c315 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
@@ -120,9 +120,9 @@
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
 import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
 import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.list_view.FakeListViewTouchTracker;
 import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker;
-import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker.ListViewTouchInfo;
 import org.chromium.components.collaboration.CollaborationService;
 import org.chromium.components.collaboration.ServiceStatus;
 import org.chromium.components.collaboration.messaging.CollaborationEvent;
@@ -1511,7 +1511,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createTouchMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -1532,7 +1532,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createMouseMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -1563,7 +1563,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createTouchMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -1584,7 +1584,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createMouseMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java
index a9e68401e..33e8777 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java
@@ -243,7 +243,7 @@
     public void onSwipeTab_Delete() {
         mItemTouchHelperCallback.onSwiped(mMockViewHolder1, POSITION1);
 
-        verify(mTabClosedListener).run(mItemView1, TAB1_ID, /* triggeringMotionEvent= */ null);
+        verify(mTabClosedListener).run(mItemView1, TAB1_ID, /* triggeringMotion= */ null);
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java
index 4d9730b..82aae74 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java
@@ -32,7 +32,6 @@
 import android.graphics.drawable.Drawable;
 import android.util.Size;
 import android.view.ContextThemeWrapper;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewStub;
@@ -69,6 +68,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.TabActionListener;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.TabActionState;
 import org.chromium.components.browser_ui.util.OnPeripheralClickListener;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** Junit Tests for {@link TabGridViewBinder}. */
@@ -486,13 +486,13 @@
                             public void run(
                                     View view,
                                     int tabId,
-                                    @Nullable MotionEvent triggeringMotionEvent) {}
+                                    @Nullable MotionEventInfo triggeringMotion) {}
 
                             @Override
                             public void run(
                                     View view,
                                     String syncId,
-                                    @Nullable MotionEvent triggeringMotionEvent) {}
+                                    @Nullable MotionEventInfo triggeringMotion) {}
                         });
         mModel.set(TabProperties.TAB_ACTION_BUTTON_DATA, tabActionButtonData);
 
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 835ac7b..43a20079 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
@@ -153,10 +153,10 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.TabActionButtonData.TabActionButtonType;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.TabActionState;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.ActionConfirmationResult;
 import org.chromium.components.browser_ui.widget.list_view.FakeListViewTouchTracker;
 import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker;
-import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker.ListViewTouchInfo;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 import org.chromium.components.collaboration.CollaborationService;
 import org.chromium.components.collaboration.ServiceStatus;
@@ -993,7 +993,7 @@
                 .run(
                         mItemView2,
                         mModelList.get(1).model.get(TabProperties.TAB_ID),
-                        /* triggeringMotionEvent= */ null);
+                        /* triggeringMotion= */ null);
 
         verify(mGridCardOnClickListenerProvider)
                 .onTabSelecting(mModelList.get(1).model.get(TabProperties.TAB_ID), true);
@@ -1011,10 +1011,9 @@
                 .run(
                         mItemView1,
                         mModelList.get(0).model.get(TabProperties.TAB_ID),
-                        /* triggeringMotionEvent= */ null);
+                        /* triggeringMotion= */ null);
 
-        verify(mOpenGroupActionListener)
-                .run(mItemView1, TAB1_ID, /* triggeringMotionEvent= */ null);
+        verify(mOpenGroupActionListener).run(mItemView1, TAB1_ID, /* triggeringMotion= */ null);
     }
 
     @Test
@@ -1029,10 +1028,9 @@
                 .run(
                         mItemView1,
                         mModelList.get(0).model.get(TabProperties.TAB_ID),
-                        /* triggeringMotionEvent= */ null);
+                        /* triggeringMotion= */ null);
 
-        verify(mOpenGroupActionListener)
-                .run(mItemView1, TAB1_ID, /* triggeringMotionEvent= */ null);
+        verify(mOpenGroupActionListener).run(mItemView1, TAB1_ID, /* triggeringMotion= */ null);
     }
 
     @Test
@@ -1046,7 +1044,7 @@
                 .run(
                         mItemView2,
                         mModelList.get(1).model.get(TabProperties.TAB_ID),
-                        /* triggeringMotionEvent= */ null);
+                        /* triggeringMotion= */ null);
 
         TabClosureParams params = TabClosureParams.closeTab(mTab2).allowUndo(true).build();
         verify(mTabRemover)
@@ -1068,7 +1066,7 @@
     }
 
     @Test
-    public void sendsCloseSignalCorrectly_TriggeringMotionEventFromMouse_DisallowUndo() {
+    public void sendsCloseSignalCorrectly_TriggeringMotionFromMouse_DisallowUndo() {
         mMediator.setActionOnAllRelatedTabsForTesting(false);
         mModelList
                 .get(1)
@@ -1078,12 +1076,13 @@
                 .run(
                         mItemView2,
                         mModelList.get(1).model.get(TabProperties.TAB_ID),
-                        TabUiTestHelper.createMouseMotionEvent(
-                                /* downTime= */ SystemClock.uptimeMillis(),
-                                /* eventTime= */ SystemClock.uptimeMillis() + 200,
-                                MotionEvent.ACTION_UP,
-                                /* x= */ 0,
-                                /* y= */ 0));
+                        MotionEventInfo.fromMotionEvent(
+                                TabUiTestHelper.createMouseMotionEvent(
+                                        /* downTime= */ SystemClock.uptimeMillis(),
+                                        /* eventTime= */ SystemClock.uptimeMillis() + 200,
+                                        MotionEvent.ACTION_UP,
+                                        /* x= */ 0,
+                                        /* y= */ 0)));
 
         verify(mTabRemover)
                 .closeTabs(
@@ -1103,7 +1102,7 @@
                 .run(
                         mItemView2,
                         mModelList.get(1).model.get(TabProperties.TAB_ID),
-                        /* triggeringMotionEvent= */ null);
+                        /* triggeringMotion= */ null);
 
         verify(mTabRemover)
                 .closeTabs(
@@ -1124,7 +1123,7 @@
                 .run(
                         mItemView2,
                         mModelList.get(1).model.get(TabProperties.TAB_ID),
-                        /* triggeringMotionEvent= */ null);
+                        /* triggeringMotion= */ null);
 
         verify(mTabRemover)
                 .closeTabs(
@@ -4037,7 +4036,7 @@
                 .get(1)
                 .model
                 .get(TabProperties.TAB_CLICK_LISTENER)
-                .run(mItemView2, TAB2_ID, /* triggeringMotionEvent= */ null);
+                .run(mItemView2, TAB2_ID, /* triggeringMotion= */ null);
         assertThat(mModelList.get(1).model.get(TabProperties.IS_SELECTED), equalTo(true));
         assertEquals(fetcher2, mModelList.get(1).model.get(TabProperties.THUMBNAIL_FETCHER));
     }
@@ -4083,7 +4082,7 @@
                 .get(1)
                 .model
                 .get(TabProperties.TAB_CLICK_LISTENER)
-                .run(mItemView2, TAB2_ID, /* triggeringMotionEvent= */ null);
+                .run(mItemView2, TAB2_ID, /* triggeringMotion= */ null);
         assertThat(mModelList.get(1).model.get(TabProperties.IS_SELECTED), equalTo(true));
         assertNotEquals(fetcher2, mModelList.get(1).model.get(TabProperties.THUMBNAIL_FETCHER));
     }
@@ -4321,7 +4320,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createTouchMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -4341,7 +4340,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createMouseMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -4370,7 +4369,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createTouchMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -4390,7 +4389,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createMouseMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -4974,7 +4973,7 @@
                 .run(
                         mItemView1,
                         mModelList.get(0).model.get(TabProperties.TAB_GROUP_SYNC_ID),
-                        /* triggeringMotionEvent= */ null);
+                        /* triggeringMotion= */ null);
 
         // Assert that the tab group has been removed from the model list and archive status reset.
         assertEquals(TAB, mModelList.get(0).model.get(CARD_TYPE));
@@ -4995,10 +4994,10 @@
                 .run(
                         mItemView1,
                         mModelList.get(0).model.get(TabProperties.TAB_GROUP_SYNC_ID),
-                        /* triggeringMotionEvent= */ null);
+                        /* triggeringMotion= */ null);
 
         verify(mOpenGroupActionListener)
-                .run(mItemView1, SYNC_GROUP_ID1, /* triggeringMotionEvent= */ null);
+                .run(mItemView1, SYNC_GROUP_ID1, /* triggeringMotion= */ null);
     }
 
     @Test
@@ -5049,7 +5048,7 @@
                 .run(
                         mItemView1,
                         mModelList.get(0).model.get(TabProperties.TAB_GROUP_SYNC_ID),
-                        /* triggeringMotionEvent= */ null);
+                        /* triggeringMotion= */ null);
         assertThat(mModelList.get(0).model.get(TabProperties.IS_SELECTED), equalTo(true));
     }
 
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
index d1d22c7..52d22a991 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -493,16 +493,12 @@
                     ColorStateList.valueOf(
                             ContextCompat.getColor(
                                     mActivity, R.color.ntp_customization_edit_icon_fill_color)));
-            FrameLayout.LayoutParams layoutParams =
-                    new FrameLayout.LayoutParams(
-                            mActivity
-                                    .getResources()
-                                    .getDimensionPixelSize(
-                                            R.dimen.ntp_customization_edit_icon_background_size),
-                            mActivity
-                                    .getResources()
-                                    .getDimensionPixelSize(
-                                            R.dimen.ntp_customization_edit_icon_background_size));
+            int size =
+                    mActivity
+                            .getResources()
+                            .getDimensionPixelSize(
+                                    R.dimen.ntp_customization_edit_icon_background_size);
+            FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(size, size);
             layoutParams.gravity = Gravity.BOTTOM | Gravity.END;
             int margin =
                     mActivity
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
index d7e74b1..ffaa29bf 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
@@ -146,6 +146,7 @@
             ChromeRenderTestRule.Builder.withPublicCorpus()
                     .setBugComponent(
                             ChromeRenderTestRule.Component.UI_BROWSER_CONTENT_SUGGESTIONS_FEED)
+                    .setRevision(1)
                     .build();
 
     public final SigninTestRule mSigninTestRule = new SigninTestRule();
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 b61811a..195499d2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -23,7 +23,6 @@
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.Menu;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewConfiguration;
@@ -284,6 +283,7 @@
 import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate;
 import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate;
 import org.chromium.components.browser_ui.util.FirstDrawDetector;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.embedder_support.util.UrlUtilities;
@@ -3224,7 +3224,7 @@
 
     @Override
     public boolean onMenuOrKeyboardAction(
-            final int id, boolean fromMenu, @Nullable MotionEvent triggeringMotionEvent) {
+            final int id, boolean fromMenu, @Nullable MotionEventInfo triggeringMotion) {
         final Tab currentTab = getActivityTab();
         boolean currentTabIsNtp = isTabNtp(currentTab);
         if (id == R.id.new_tab_menu_id) {
@@ -3328,7 +3328,7 @@
             }
             RecordUserAction.record("MobileMenuRecentTabs");
         } else if (id == R.id.close_tab) {
-            // TODO(crbug.com/375468032): use triggeringMotionEvent to decide
+            // TODO(crbug.com/375468032): use triggeringMotion to decide
             // TabClosureParams.allowUndo.
             getCurrentTabModel()
                     .getTabRemover()
@@ -3336,7 +3336,7 @@
                             TabClosureParams.closeTab(currentTab).build(), /* allowDialog= */ true);
             RecordUserAction.record("MobileTabClosed");
         } else if (id == R.id.close_all_tabs_menu_id) {
-            boolean allowUndo = TabClosureParamsUtils.shouldAllowUndo(triggeringMotionEvent);
+            boolean allowUndo = TabClosureParamsUtils.shouldAllowUndo(triggeringMotion);
 
             // Close both incognito and normal tabs.
             Runnable closeAllTabsRunnable =
@@ -3351,7 +3351,7 @@
                     closeAllTabsRunnable);
             RecordUserAction.record("MobileMenuCloseAllTabs");
         } else if (id == R.id.close_all_incognito_tabs_menu_id) {
-            boolean allowUndo = TabClosureParamsUtils.shouldAllowUndo(triggeringMotionEvent);
+            boolean allowUndo = TabClosureParamsUtils.shouldAllowUndo(triggeringMotion);
 
             // Close only incognito tabs
             Runnable closeAllTabsRunnable =
@@ -3439,7 +3439,7 @@
                     NtpCustomizationCoordinator.EntryPointType.MAIN_MENU);
             RecordUserAction.record("MobileMenuNtpCustomization");
         } else {
-            return super.onMenuOrKeyboardAction(id, fromMenu, triggeringMotionEvent);
+            return super.onMenuOrKeyboardAction(id, fromMenu, triggeringMotion);
         }
         return true;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 6529eb7..88022d42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -25,7 +25,6 @@
 import android.util.TypedValue;
 import android.view.Display.Mode;
 import android.view.MenuItem;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
@@ -201,6 +200,7 @@
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
 import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
 import org.chromium.components.browser_ui.settings.SettingsNavigation;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler.Type;
@@ -1883,13 +1883,11 @@
 
     @Override
     public boolean onOptionsItemSelected(
-            int itemId,
-            @Nullable Bundle menuItemData,
-            @Nullable MotionEvent triggeringMotionEvent) {
+            int itemId, @Nullable Bundle menuItemData, @Nullable MotionEventInfo triggeringMotion) {
         if (mManualFillingComponentSupplier.hasValue()) {
             mManualFillingComponentSupplier.get().dismiss();
         }
-        return onMenuOrKeyboardAction(itemId, /* fromMenu= */ true, triggeringMotionEvent);
+        return onMenuOrKeyboardAction(itemId, /* fromMenu= */ true, triggeringMotion);
     }
 
     @Override
@@ -2398,7 +2396,7 @@
 
     @Override
     public boolean onMenuOrKeyboardAction(
-            int id, boolean fromMenu, @Nullable MotionEvent triggeringMotionEvent) {
+            int id, boolean fromMenu, @Nullable MotionEventInfo triggeringMotion) {
         for (MenuOrKeyboardActionController.MenuOrKeyboardActionHandler handler :
                 mMenuActionHandlers) {
             if (handler.handleMenuOrKeyboardAction(id, fromMenu)) return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/backup/ChromeBackupAgentImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/backup/ChromeBackupAgentImpl.java
index a6b1f4d..a1937b15 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/backup/ChromeBackupAgentImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/backup/ChromeBackupAgentImpl.java
@@ -142,7 +142,6 @@
 
     // Bool entries from SharedPreferences that should be backed up / restored.
     static final String[] BACKUP_ANDROID_BOOL_PREFS = {
-        ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED,
         ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE,
         ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE,
         ChromePreferenceKeys.PRIVACY_METRICS_REPORTING_PERMITTED_BY_POLICY,
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 f17be45..ed378ece 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
@@ -344,7 +344,8 @@
                     BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding(
                             activity.getResources()
                                     .getQuantityString(
-                                            R.plurals.move_tab_to_another_window,
+                                            R.plurals
+                                                    .move_group_to_another_window_context_menu_item,
                                             MultiWindowUtils.getInstanceCount()),
                             R.id.move_to_other_window_menu_id,
                             isIncognito,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
index a016dd4..3311145 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -17,7 +17,6 @@
 import android.os.Bundle;
 import android.util.Pair;
 import android.view.KeyEvent;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
@@ -126,6 +125,7 @@
 import org.chromium.chrome.browser.webapps.WebappDeferredStartupWithStorageHandler;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.share.ShareHelper;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.embedder_support.delegate.WebContentsDelegateAndroid;
 
@@ -533,7 +533,8 @@
             mCustomTabObserver =
                     new CustomTabObserver(
                             mIntentDataProvider.isOpenedByChrome(),
-                            mIntentDataProvider.getSession());
+                            mIntentDataProvider.getSession(),
+                            mIntentDataProvider.getTwaStartupUptimeMillis());
             mCustomTabNavigationEventObserver =
                     new CustomTabNavigationEventObserver(
                             mIntentDataProvider.getSession(), /* forPrerender= */ false);
@@ -1129,7 +1130,7 @@
 
     @Override
     public boolean onMenuOrKeyboardAction(
-            int id, boolean fromMenu, @Nullable MotionEvent triggeringMotionEvent) {
+            int id, boolean fromMenu, @Nullable MotionEventInfo triggeringMotion) {
         // Disable creating new tabs, bookmark, print, help, focus_url, etc.
         if (id == R.id.focus_url_bar
                 || id == R.id.all_bookmarks_menu_id
@@ -1139,7 +1140,7 @@
                 || id == R.id.new_tab_menu_id) {
             return true;
         }
-        return super.onMenuOrKeyboardAction(id, fromMenu, triggeringMotionEvent);
+        return super.onMenuOrKeyboardAction(id, fromMenu, triggeringMotion);
     }
 
     public WebContentsDelegateAndroid getWebContentsDelegate() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index b1e381f..69c449f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -63,6 +63,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TrustedCdn;
 import org.chromium.chrome.browser.ui.google_bottom_bar.GoogleBottomBarCoordinator;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.page_info.PageInfoController.OpenedFromSource;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContents;
@@ -305,9 +306,7 @@
 
     @Override
     public boolean onOptionsItemSelected(
-            int itemId,
-            @Nullable Bundle menuItemData,
-            @Nullable MotionEvent triggeringMotionEvent) {
+            int itemId, @Nullable Bundle menuItemData, @Nullable MotionEventInfo triggeringMotion) {
         int menuIndex =
                 CustomTabAppMenuPropertiesDelegate.getIndexOfMenuItemFromBundle(menuItemData);
         if (menuIndex >= 0) {
@@ -321,12 +320,12 @@
             return true;
         }
 
-        return super.onOptionsItemSelected(itemId, menuItemData, triggeringMotionEvent);
+        return super.onOptionsItemSelected(itemId, menuItemData, triggeringMotion);
     }
 
     @Override
     public boolean onMenuOrKeyboardAction(
-            int id, boolean fromMenu, @Nullable MotionEvent triggeringMotionEvent) {
+            int id, boolean fromMenu, @Nullable MotionEventInfo triggeringMotion) {
         if (id == R.id.bookmark_this_page_id) {
             mTabBookmarkerSupplier.get().addOrEditBookmark(getActivityTab());
             RecordUserAction.record("MobileMenuAddToBookmarks");
@@ -372,7 +371,7 @@
             }
             return true;
         }
-        return super.onMenuOrKeyboardAction(id, fromMenu, triggeringMotionEvent);
+        return super.onMenuOrKeyboardAction(id, fromMenu, triggeringMotion);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
index 32f01eab..8f75b84 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -244,6 +244,13 @@
             "androidx.browser.customtabs.extra.INITIAL_ACTIVITY_HEIGHT_IN_PIXEL";
 
     /**
+     * Extra that, if set, corresponds to the timestamp (in SystemClock.uptimeMillis()) when the TWA
+     * launcher Activity was created. Used to measure the full startup duration.
+     */
+    public static final String EXTRA_TWA_STARTUP_UPTIME_MS =
+            "org.chromium.chrome.browser.customtabs.trusted.STARTUP_UPTIME_MILLIS";
+
+    /**
      * Extra that, if set, allows you to interact with the background app when a PCCT is launched.
      * Note: Deprecated. Use {@link CustomTabsIntent#isBackgroundInteractionEnabled(Intent)}.
      */
@@ -1718,4 +1725,11 @@
 
         return DisplayMode.STANDALONE;
     }
+
+    @Override
+    public @Nullable Long getTwaStartupUptimeMillis() {
+        if (!isTrustedWebActivity()) return null;
+        long value = IntentUtils.safeGetLongExtra(getIntent(), EXTRA_TWA_STARTUP_UPTIME_MS, 0);
+        return value != 0 ? Long.valueOf(value) : null;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
index 0b752f9..f460797 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
@@ -59,6 +59,10 @@
 
     // The time of the first navigation commit in the most recent Custom Tab launch.
     private long mFirstCommitRealtimeMillis;
+    private long mFirstCommitUptimeMillis;
+
+    // The TWA startup timestamp
+    private final Long mTwaStartupUptimeMillis;
 
     // Lets Long press on links select the link text instead of triggering context menu.
     private boolean mLongPressLinkSelectText;
@@ -99,10 +103,11 @@
         }
     }
 
-    public CustomTabObserver(boolean openedByChrome, SessionHolder<?> token) {
-        boolean mOpenedByChrome = openedByChrome;
-        mCustomTabsConnection = mOpenedByChrome ? null : CustomTabsConnection.getInstance();
+    public CustomTabObserver(
+            boolean openedByChrome, SessionHolder<?> token, Long twaStartupUptimeMillis) {
+        mCustomTabsConnection = openedByChrome ? null : CustomTabsConnection.getInstance();
         mSession = token;
+        mTwaStartupUptimeMillis = twaStartupUptimeMillis;
         resetPageLoadTracking();
     }
 
@@ -225,38 +230,54 @@
         if (!isFirstMainFrameCommit) return;
 
         mFirstCommitRealtimeMillis = SystemClock.elapsedRealtime();
+        mFirstCommitUptimeMillis = SystemClock.uptimeMillis();
 
         recordFirstCommitNavigation();
     }
 
     private void recordFirstCommitNavigation() {
         if (mCustomTabsConnection == null) return;
-        String histogram = null;
+        String suffix = null;
         long duration = 0;
         // Note that this will exclude Webapp launches in all cases due to either
         // mUsedHiddenTabSpeculation being null, or mIntentReceivedTimestamp being 0.
         if (mUsedHiddenTabSpeculation != null && mUsedHiddenTabSpeculation) {
             duration = mFirstCommitRealtimeMillis - mLaunchedForSpeculationRealtimeMillis;
-            histogram = "CustomTabs.Startup.TimeToFirstCommitNavigation2.Speculated";
+            suffix = ".Speculated";
         } else if (mIntentReceivedRealtimeMillis > 0) {
             // When the process is already warm the earliest measurable point in startup is when the
             // intent is received so we measure from there. In the cold start case we measure from
             // when the process was started as the best comparison against the warm case.
             if (wasWarmedUp()) {
                 duration = mFirstCommitRealtimeMillis - mIntentReceivedRealtimeMillis;
-                histogram = "CustomTabs.Startup.TimeToFirstCommitNavigation2.WarmedUp";
+                suffix = ".WarmedUp";
             } else if (ColdStartTracker.wasColdOnFirstActivityCreationOrNow()
                     && SimpleStartupForegroundSessionDetector.runningCleanForegroundSession()) {
                 duration = mFirstCommitRealtimeMillis - Process.getStartElapsedRealtime();
-                histogram = "CustomTabs.Startup.TimeToFirstCommitNavigation2.Cold";
+                suffix = ".Cold";
             } else {
                 duration = mFirstCommitRealtimeMillis - mIntentReceivedRealtimeMillis;
-                histogram = "CustomTabs.Startup.TimeToFirstCommitNavigation2.Warm";
+                suffix = ".Warm";
             }
         }
-        if (histogram != null) {
+        if (suffix != null) {
             RecordHistogram.recordCustomTimesHistogram(
-                    histogram, duration, 50, DateUtils.MINUTE_IN_MILLIS, 50);
+                    "CustomTabs.Startup.TimeToFirstCommitNavigation2" + suffix,
+                    duration,
+                    50,
+                    DateUtils.MINUTE_IN_MILLIS,
+                    50);
+            // For TWA startup, the recorded duration is the difference between
+            // mFirstCommitUptimeMillis and mTwaStartupUptimeMillis, regardless
+            // of the suffix.
+            if (mTwaStartupUptimeMillis != null) {
+                RecordHistogram.recordCustomTimesHistogram(
+                        "TrustedWebActivity.Startup.TimeToFirstCommitNavigation2" + suffix,
+                        mFirstCommitUptimeMillis - mTwaStartupUptimeMillis.longValue(),
+                        50,
+                        DateUtils.MINUTE_IN_MILLIS,
+                        50);
+            }
         }
     }
 
@@ -270,32 +291,47 @@
 
     private void recordPaint(long paintUptimeMillis, String paintMetricName) {
         if (mCustomTabsConnection == null) return;
-        String histogram = null;
+        String suffix = null;
         long duration = 0;
         // Note that this will exclude Webapp launches in all cases due to either
         // mUsedHiddenTabSpeculation being null, or mIntentReceivedTimestamp being 0.
         if (mUsedHiddenTabSpeculation != null && mUsedHiddenTabSpeculation) {
             duration = paintUptimeMillis - mLaunchedForSpeculationUptimeMillis;
-            histogram = "CustomTabs.Startup." + paintMetricName + ".Speculated";
+            suffix = ".Speculated";
         } else if (mIntentReceivedRealtimeMillis > 0) {
             // When the process is already warm the earliest measurable point in startup is when the
             // intent is received so we measure from there. In the cold start case we measure from
             // when the process was started as the best comparison against the warm case.
             if (wasWarmedUp()) {
                 duration = paintUptimeMillis - mIntentReceivedUptimeMillis;
-                histogram = "CustomTabs.Startup." + paintMetricName + ".WarmedUp";
+                suffix = ".WarmedUp";
             } else if (ColdStartTracker.wasColdOnFirstActivityCreationOrNow()
                     && SimpleStartupForegroundSessionDetector.runningCleanForegroundSession()) {
                 duration = paintUptimeMillis - Process.getStartUptimeMillis();
-                histogram = "CustomTabs.Startup." + paintMetricName + ".Cold";
+                suffix = ".Cold";
             } else {
                 duration = paintUptimeMillis - mIntentReceivedUptimeMillis;
-                histogram = "CustomTabs.Startup." + paintMetricName + ".Warm";
+                suffix = ".Warm";
             }
         }
-        if (histogram != null) {
+        if (suffix != null) {
             RecordHistogram.recordCustomTimesHistogram(
-                    histogram, duration, 50, DateUtils.MINUTE_IN_MILLIS, 50);
+                    "CustomTabs.Startup." + paintMetricName + suffix,
+                    duration,
+                    50,
+                    DateUtils.MINUTE_IN_MILLIS,
+                    50);
+            // For TWA startup, the recorded duration is the difference between
+            // mFirstCommitUptimeMillis and mTwaStartupUptimeMillis, regardless
+            // of the suffix.
+            if (mTwaStartupUptimeMillis != null) {
+                RecordHistogram.recordCustomTimesHistogram(
+                        "TrustedWebActivity.Startup." + paintMetricName + suffix,
+                        paintUptimeMillis - mTwaStartupUptimeMillis.longValue(),
+                        50,
+                        DateUtils.MINUTE_IN_MILLIS,
+                        50);
+            }
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
index 29c86a5..92790f90 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
@@ -230,7 +230,8 @@
 
         TabObserverRegistrar registrar = new TabObserverRegistrar();
         CustomTabObserver customTabObserver =
-                new CustomTabObserver(/* openedByChrome= */ false, session);
+                new CustomTabObserver(
+                        /* openedByChrome= */ false, session, /* twaStartupUptimeMillis= */ null);
         CustomTabNavigationEventObserver customTabNavigationEventObserver =
                 new CustomTabNavigationEventObserver(session, /* forPrerender= */ true);
         CustomTabActivityTabController.addTabNavigationObservers(
@@ -395,7 +396,8 @@
         TabObserverRegistrar registrar = new TabObserverRegistrar();
         SessionHolder<?> token = SessionHolder.getSessionHolderFromIntent(intent);
         CustomTabObserver customTabObserver =
-                new CustomTabObserver(/* openedByChrome= */ false, token);
+                new CustomTabObserver(
+                        /* openedByChrome= */ false, token, /* twaStartupUptimeMillis= */ null);
         CustomTabNavigationEventObserver customTabNavigationEventObserver =
                 new CustomTabNavigationEventObserver(token, /* forPrerender= */ false);
         CustomTabActivityTabController.addTabNavigationObservers(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
index 5c9a79e4..b257907 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
@@ -32,15 +32,9 @@
 /** A {@link Fragment} that presents a set of search engines for the user to choose from. */
 @NullMarked
 public class DefaultSearchEngineFirstRunFragment extends Fragment implements FirstRunFragment {
-    @SearchEnginePromoType private int mSearchEnginePromoDialogType;
+    private @SearchEnginePromoType int mSearchEnginePromoDialogType;
     private boolean mShownRecorded;
 
-    /** Layout that displays the available search engines to the user. */
-    private RadioButtonLayout mEngineLayout;
-
-    /** The button that lets a user proceed to the next page after an engine is selected. */
-    private Button mButton;
-
     @Override
     public View onCreateView(
             LayoutInflater inflater,
@@ -49,13 +43,16 @@
         View rootView =
                 inflater.inflate(
                         R.layout.default_search_engine_first_run_fragment, container, false);
-        mEngineLayout = rootView.findViewById(R.id.default_search_engine_dialog_options);
-        mButton = rootView.findViewById(R.id.button_primary);
-        mButton.setEnabled(false);
+        // Layout that displays the available search engines to the user.
+        RadioButtonLayout engineLayout =
+                rootView.findViewById(R.id.default_search_engine_dialog_options);
+        // The button that lets a user proceed to the next page after an engine is selected.
+        Button button = rootView.findViewById(R.id.button_primary);
+        button.setEnabled(false);
 
         ((TextView) rootView.findViewById(R.id.footer))
                 .setText(R.string.search_engine_dialog_footer);
-        mButton.setText(R.string.search_engine_dialog_confirm_button_title);
+        button.setText(R.string.search_engine_dialog_confirm_button_title);
 
         FirstRunPageDelegate delegate = assumeNonNull(getPageDelegate());
         Profile profile = delegate.getProfileProviderSupplier().get().getOriginalProfile();
@@ -66,8 +63,8 @@
             new DefaultSearchEngineDialogHelper(
                     mSearchEnginePromoDialogType,
                     LocaleManager.getInstance(),
-                    mEngineLayout,
-                    mButton,
+                    engineLayout,
+                    button,
                     getPageDelegate()::advanceToNextPage);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
index e7955c9..b5b8873 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
@@ -694,11 +694,6 @@
     }
 
     @Override
-    public boolean didAcceptTermsOfService() {
-        return FirstRunUtils.didAcceptTermsOfService();
-    }
-
-    @Override
     public boolean isLaunchedFromCct() {
         return mLaunchedFromCct;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunPageDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunPageDelegate.java
index cbe05a18..de4680aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunPageDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunPageDelegate.java
@@ -44,16 +44,11 @@
      * run activity and start the main activity without setting any of the preferences tracking
      * whether first run has been completed.
      *
-     * Exposing this function is intended for use in scenarios where FRE is partially or completely
-     * skipped. (e.g. in accordance with Enterprise polices)
+     * <p>Exposing this function is intended for use in scenarios where FRE is partially or
+     * completely skipped. (e.g. in accordance with Enterprise polices)
      */
     void exitFirstRun();
 
-    /**
-     * @return Whether the user has accepted Chrome Terms of Service.
-     */
-    boolean didAcceptTermsOfService();
-
     /** Returns whether chrome is launched as a custom tab. */
     boolean isLaunchedFromCct();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
index c6ef7127..cec9010 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
@@ -9,12 +9,9 @@
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.ResettersForTesting;
-import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.browser.metrics.ChangeMetricsReportingStateCalledFrom;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
-import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
-import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
 import org.chromium.ui.accessibility.AccessibilityState;
 
 /** Provides first run related utility functions. */
@@ -25,50 +22,27 @@
     private static boolean sDisableDelayOnExitFreForTest;
 
     /**
-     * Synchronizes first run native and Java preferences.
-     * Must be called after native initialization.
+     * Synchronizes first run native and Java preferences. Must be called after native
+     * initialization.
      */
     public static void cacheFirstRunPrefs() {
-        SharedPreferencesManager javaPrefs = ChromeSharedPreferences.getInstance();
-        // Set both Java and native prefs if any of the three indicators indicate ToS has been
-        // accepted. This needed because:
-        //   - Old versions only set native pref, so this syncs Java pref.
-        //   - Backup & restore does not restore native pref, so this needs to update it.
-        //   - checkAnyUserHasSeenToS() may be true which needs to sync its state to the prefs.
-        boolean javaPrefValue =
-                javaPrefs.readBoolean(ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED, false);
-        boolean nativePrefValue = isFirstRunEulaAccepted();
-        boolean isFirstRunComplete = FirstRunStatus.getFirstRunFlowComplete();
-        if (javaPrefValue || nativePrefValue || isFirstRunComplete) {
-            if (!javaPrefValue) {
-                javaPrefs.writeBoolean(ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED, true);
-            }
-            if (!nativePrefValue) {
-                setEulaAccepted();
-            }
+        // Backup and restore does not restore native pref, so this needs to update it. Note that
+        // these prefs are slightly different, the eula is set when the ToS is accepted (early in
+        // the FRE), while the FRE flow is only complete at the end.
+        if (FirstRunStatus.getFirstRunFlowComplete() && !isFirstRunEulaAccepted()) {
+            setEulaAccepted();
         }
     }
 
     /**
-     * @return Whether the user has accepted Chrome Terms of Service.
-     */
-    public static boolean didAcceptTermsOfService() {
-        // Note: Does not check FirstRunUtils.isFirstRunEulaAccepted() because this may be called
-        // before native is initialized.
-        return ChromeSharedPreferences.getInstance()
-                .readBoolean(ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED, false);
-    }
-
-    /**
      * Sets the EULA/Terms of Services state as "ACCEPTED".
+     *
      * @param allowMetricsAndCrashUploading True if the user allows to upload crash dumps and
-     *         collect stats.
+     *     collect stats.
      */
     static void acceptTermsOfService(boolean allowMetricsAndCrashUploading) {
         UmaSessionStats.changeMetricsReportingConsent(
                 allowMetricsAndCrashUploading, ChangeMetricsReportingStateCalledFrom.UI_FIRST_RUN);
-        ChromeSharedPreferences.getInstance()
-                .writeBoolean(ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED, true);
         setEulaAccepted();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/new_tab_url/DseNewTabUrlManager.java b/chrome/android/java/src/org/chromium/chrome/browser/new_tab_url/DseNewTabUrlManager.java
index 6ac3c7c..4914a13 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/new_tab_url/DseNewTabUrlManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/new_tab_url/DseNewTabUrlManager.java
@@ -14,6 +14,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.regional_capabilities.RegionalCapabilitiesServiceFactory;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
+import org.chromium.components.omnibox.OmniboxFeatures;
 import org.chromium.components.regional_capabilities.RegionalCapabilitiesService;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.components.search_engines.TemplateUrlService;
@@ -54,7 +55,8 @@
     /** Returns whether the feature NewTabSearchEngineUrlAndroid is enabled. */
     public static boolean isNewTabSearchEngineUrlAndroidEnabled() {
         return ChromeSharedPreferences.getInstance()
-                .readBoolean(ChromePreferenceKeys.IS_EEA_CHOICE_COUNTRY, false);
+                        .readBoolean(ChromePreferenceKeys.IS_EEA_CHOICE_COUNTRY, false)
+                || OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 0d1d3997..33bc94a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -41,6 +41,8 @@
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.Supplier;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.app.feed.FeedActionDelegateImpl;
 import org.chromium.chrome.browser.back_press.BackPressMetrics;
@@ -847,7 +849,8 @@
 
     private void onSearchEngineUpdated() {
         updateSearchProviderHasLogo();
-        mNewTabPageLayout.updateSearchBoxHintText();
+
+        PostTask.postTask(TaskTraits.UI_DEFAULT, mNewTabPageLayout::updateSearchBoxHintText);
         setSearchProviderInfoOnView(
                 mSearchProviderHasLogo, mTemplateUrlService.isDefaultSearchEngineGoogle());
         // TODO(crbug.com/40226731): Remove this call when the Feed position experiment is
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index c507706..33ab778 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -650,7 +650,9 @@
                 isIncognito
                         ? R.string.hub_search_empty_hint_incognito
                         : R.string.hub_search_empty_hint;
-        mLocationBarCoordinator.getUrlBarCoordinator().setUrlBarHintText(hintTextRes);
+        mLocationBarCoordinator
+                .getUrlBarCoordinator()
+                .setUrlBarHintText(getResources().getString(hintTextRes));
     }
 
     /* package */ boolean loadUrl(@NonNull OmniboxLoadUrlParams params, boolean isIncognito) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/segmentation_platform/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/segmentation_platform/OWNERS
index ffc05827..a16b499 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/segmentation_platform/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/segmentation_platform/OWNERS
@@ -1 +1,2 @@
-shaktisahu@chromium.org
+salg@google.com
+file://components/segmentation_platform/OWNERS
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/PolicyLoadListener.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/PolicyLoadListener.java
index 4a392261..a780836c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/PolicyLoadListener.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/PolicyLoadListener.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.signin;
 
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
 import org.chromium.base.CallbackController;
@@ -44,13 +43,6 @@
      */
     private @Nullable Boolean mHasRestriction;
 
-    @VisibleForTesting
-    public PolicyLoadListener() {
-        mCallbackController = null;
-        mMightHavePoliciesSupplier = null;
-        mPolicyServiceSupplier = null;
-    }
-
     /**
      * Create the instance and start listening to signals from policy service and app restrictions.
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
index 54b94b95..e3ac8f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -262,6 +262,7 @@
     private int mRootId;
     private @Nullable Token mTabGroupId;
     private boolean mTabHasSensitiveContent;
+    private boolean mIsPinned;
     private @TabUserAgent int mUserAgent = TabUserAgent.DEFAULT;
 
     /**
@@ -1286,6 +1287,7 @@
         setTabGroupId(state.tabGroupId);
         setUserAgent(state.userAgent);
         setTabHasSensitiveContent(state.tabHasSensitiveContent);
+        setIsPinned(state.isPinned);
     }
 
     /**
@@ -2586,6 +2588,20 @@
     }
 
     @Override
+    public boolean getIsPinned() {
+        return mIsPinned;
+    }
+
+    @Override
+    public void setIsPinned(boolean isPinned) {
+        if (mIsPinned == isPinned || isDestroyed()) return;
+        mIsPinned = isPinned;
+        for (TabObserver observer : mObservers) {
+            observer.onTabPinnedStateChanged(this, isPinned);
+        }
+    }
+
+    @Override
     public void onTabRestoredFromArchivedTabModel() {
         for (TabObserver observer : mObservers) {
             observer.onTabUnarchived(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateExtractor.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateExtractor.java
index a4aaf27..7a3f44b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateExtractor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateExtractor.java
@@ -49,6 +49,7 @@
                 tab.getLastNavigationCommittedTimestampMillis();
         tabState.tabGroupId = tab.getTabGroupId();
         tabState.tabHasSensitiveContent = tab.getTabHasSensitiveContent();
+        tabState.isPinned = tab.getIsPinned();
         return tabState;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index ca62197..7c3eec8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -89,6 +89,7 @@
 import org.chromium.chrome.browser.merchant_viewer.MerchantTrustSignalsCoordinator;
 import org.chromium.chrome.browser.metrics.UmaActivityObserver;
 import org.chromium.chrome.browser.multiwindow.MultiInstanceManager;
+import org.chromium.chrome.browser.ntp.IncognitoNewTabPage;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.offlinepages.OfflinePageTabData;
@@ -1664,9 +1665,10 @@
                 mLayoutStateProvider != null
                         ? mLayoutStateProvider.getActiveLayoutType() == LayoutType.TAB_SWITCHER
                         : false);
-        KeyboardAccessoryStateSupplier keyboardAccessoryHeightSupplier =
+        KeyboardAccessoryStateSupplier keyboardAccessoryStateSupplier =
                 new KeyboardAccessoryStateSupplier(
-                        ManualFillingComponentSupplier.from(mWindowAndroid));
+                        ManualFillingComponentSupplier.from(mWindowAndroid),
+                        mControlContainer.getView());
         ObservableSupplierImpl<Integer> controlContainerTranslationSupplier =
                 new ObservableSupplierImpl<>(0);
         new ToolbarPositionController(
@@ -1677,7 +1679,7 @@
                 mOmniboxFocusStateSupplier,
                 mFormFieldFocusedSupplier,
                 mFindInPageShowingSupplier,
-                keyboardAccessoryHeightSupplier,
+                keyboardAccessoryStateSupplier,
                 mWindowAndroid.getKeyboardDelegate(),
                 mControlContainer,
                 mBottomControlsStacker,
@@ -1697,9 +1699,7 @@
                             mBrowserControlsSizer,
                             mWindowAndroid.getInsetObserver(),
                             controlContainerTranslationSupplier,
-                            () ->
-                                    keyboardAccessoryHeightSupplier.isSheetShowing(
-                                            mControlContainer.getView()));
+                            keyboardAccessoryStateSupplier.getIsSheetShowingSupplier());
         }
     }
 
@@ -1830,6 +1830,11 @@
         }
 
         @Override
+        public boolean isIncognitoNewTabPageCurrentlyVisible() {
+            return getIncognitoNewTabPageForCurrentTab() != null;
+        }
+
+        @Override
         public boolean dispatchTouchEvent(MotionEvent ev) {
             assert mVisibleNtp != null;
             // No null check -- the toolbar should not be moved if we are not on an NTP.
@@ -1934,6 +1939,15 @@
         return null;
     }
 
+    private IncognitoNewTabPage getIncognitoNewTabPageForCurrentTab() {
+        if (mLocationBarModel.hasTab()) {
+            NativePage nativePage = mLocationBarModel.getTab().getNativePage();
+            if (nativePage instanceof IncognitoNewTabPage incognitoNewTabPage)
+                return incognitoNewTabPage;
+        }
+        return null;
+    }
+
     /**
      * @return Whether the UrlBar currently has focus.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 2606ae2..67c2bdb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -10,7 +10,6 @@
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
-import android.view.MotionEvent;
 
 import androidx.browser.customtabs.CustomTabsIntent;
 
@@ -23,6 +22,7 @@
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.intents.WebappIntentUtils;
 import org.chromium.chrome.browser.customtabs.BaseCustomTabActivity;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 
 /** Displays a webapp in a nearly UI-less Chrome (InfoBars still appear). */
 public class WebappActivity extends BaseCustomTabActivity {
@@ -69,7 +69,7 @@
 
     @Override
     public boolean onMenuOrKeyboardAction(
-            int id, boolean fromMenu, @Nullable MotionEvent triggeringMotionEvent) {
+            int id, boolean fromMenu, @Nullable MotionEventInfo triggeringMotion) {
         // Disable creating bookmark.
         if (id == R.id.bookmark_this_page_id) {
             return true;
@@ -83,7 +83,7 @@
             }
             return true;
         }
-        return super.onMenuOrKeyboardAction(id, fromMenu, triggeringMotionEvent);
+        return super.onMenuOrKeyboardAction(id, fromMenu, triggeringMotion);
     }
 
     @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabTest.java
index a49c912..ed5a311 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabTest.java
@@ -251,6 +251,7 @@
         tabState.rootId = 5;
         tabState.tabGroupId = new Token(1L, 2L);
         tabState.tabHasSensitiveContent = true;
+        tabState.isPinned = true;
 
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
@@ -263,6 +264,7 @@
         assertEquals(tabState.rootId, mTab.getRootId());
         assertEquals(tabState.tabGroupId, mTab.getTabGroupId());
         assertEquals(tabState.tabHasSensitiveContent, mTab.getTabHasSensitiveContent());
+        assertEquals(tabState.isPinned, mTab.getIsPinned());
     }
 
     @FunctionalInterface
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
index 9bfad54..bcc0768 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
@@ -798,7 +798,7 @@
         startActivityNormally();
 
         mActivityTestRule.loadUrlInNewTab(UrlConstants.NTP_URL, /* incognito= */ true);
-        onView(withId(R.id.location_bar_status_icon)).check(matches(not(isDisplayed())));
+        onView(withId(R.id.location_bar_status_icon)).check(matches(isDisplayed()));
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
index 1f4c1505..f07152a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
@@ -24,6 +24,7 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.EnormousTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -101,6 +102,7 @@
     @Test
     @MediumTest
     @Feature({"Omnibox"})
+    @DisabledTest(message = "crbug.com/419016470 - likely addressed, but can't trigger bot from CQ")
     public void testDefaultText() {
         mActivityTestRule.startOnNtp();
 
@@ -114,7 +116,7 @@
                 mActivityTestRule
                         .getActivity()
                         .getResources()
-                        .getString(R.string.omnibox_empty_hint),
+                        .getString(R.string.omnibox_empty_hint_with_dse_name, "Google"),
                 urlBar.getHint().toString());
 
         // Type something in the omnibox.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/TabStateFlatBufferTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/TabStateFlatBufferTest.java
index 99a433d..c24df82 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/TabStateFlatBufferTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/TabStateFlatBufferTest.java
@@ -381,6 +381,7 @@
         state.lastNavigationCommittedTimestampMillis = 42L;
         state.timestampMillis = 41L;
         state.tabHasSensitiveContent = true;
+        state.isPinned = true;
         state.isIncognito = isIncognito;
         int capacity = 100;
         byte[] bytes = new byte[capacity];
@@ -426,6 +427,7 @@
         Assert.assertEquals(expected.timestampMillis, actual.timestampMillis);
         Assert.assertEquals(expected.themeColor, actual.themeColor);
         Assert.assertEquals(expected.tabHasSensitiveContent, actual.tabHasSensitiveContent);
+        Assert.assertEquals(expected.isPinned, actual.isPinned);
         ByteBufferTestUtils.verifyByteBuffer(
                 expected.contentsState.buffer(), actual.contentsState.buffer());
         // Don't assert on the fields of the WebContentsState as it is random data in this test.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawableRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawableRenderTest.java
index f0af1bef..1c315bbb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawableRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawableRenderTest.java
@@ -46,7 +46,7 @@
     public final ChromeRenderTestRule mRenderTestRule =
             ChromeRenderTestRule.Builder.withPublicCorpus()
                     .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE_TAB_GROUPS)
-                    .setRevision(2)
+                    .setRevision(3)
                     .build();
 
     @ClassRule
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/backup/ChromeBackupAgentTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/backup/ChromeBackupAgentTest.java
index 18c07c86..0c166c38 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/backup/ChromeBackupAgentTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/backup/ChromeBackupAgentTest.java
@@ -173,7 +173,6 @@
         // but ChromeBackupAgentImpl is agnostic to that. The focus of these tests is making sure
         // that all the allowlisted prefs are backed up, and none other.
         editor.putBoolean(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE, true);
-        editor.putBoolean(ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED, false);
         editor.putBoolean(ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE, false);
         editor.putBoolean(ChromePreferenceKeys.PRIVACY_METRICS_REPORTING_PERMITTED_BY_USER, false);
         editor.putBoolean(
@@ -281,9 +280,6 @@
                         "AndroidDefault." + ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE, 1);
         verify(backupData)
                 .writeEntityHeader(
-                        "AndroidDefault." + ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED, 1);
-        verify(backupData)
-                .writeEntityHeader(
                         "AndroidDefault."
                                 + ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE,
                         1);
@@ -326,11 +322,6 @@
                     names,
                     hasItem(
                             "AndroidDefault."
-                                    + ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED));
-            assertThat(
-                    names,
-                    hasItem(
-                            "AndroidDefault."
                                     + ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE));
             assertThat(
                     names,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java
index 5a46ae9a..44536352 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java
@@ -53,9 +53,9 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabOverflowMenuCoordinator.OnItemClickedCallback;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.list_view.FakeListViewTouchTracker;
 import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker;
-import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker.ListViewTouchInfo;
 import org.chromium.components.collaboration.CollaborationService;
 import org.chromium.components.collaboration.ServiceStatus;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
@@ -462,7 +462,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createTouchMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -479,7 +479,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createMouseMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java
index ef5c2fe..b64adf3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java
@@ -60,9 +60,9 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabOverflowMenuCoordinator.OnItemClickedCallback;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.list_view.FakeListViewTouchTracker;
 import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker;
-import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker.ListViewTouchInfo;
 import org.chromium.components.collaboration.CollaborationService;
 import org.chromium.components.collaboration.ServiceStatus;
 import org.chromium.components.data_sharing.member_role.MemberRole;
@@ -358,7 +358,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createTouchMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -379,7 +379,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createMouseMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -410,7 +410,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createTouchMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -431,7 +431,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker listViewTouchTracker = new FakeListViewTouchTracker();
         listViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         TabUiTestHelper.createMouseMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java
index f74dd91..80c31f7b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceMediatorTest.java
@@ -72,6 +72,7 @@
 import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
 import org.chromium.components.browser_ui.widget.displaystyle.UiConfig.DisplayStyle;
 import org.chromium.components.browser_ui.widget.displaystyle.VerticalDisplayStyle;
+import org.chromium.components.omnibox.OmniboxFeatureList;
 import org.chromium.components.prefs.PrefChangeRegistrar;
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.search_engines.TemplateUrlService;
@@ -378,6 +379,7 @@
     }
 
     @Test
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void testWithEeaCountryOnlyEnabled() {
         PropertyModel model = SectionHeaderListProperties.create(TOOLBAR_HEIGHT);
         doReturn(false).when(mUrlService).isDefaultSearchEngineGoogle();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/new_tab_url/DseNewTabUrlManagerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/new_tab_url/DseNewTabUrlManagerUnitTest.java
index 1a7d678..5b4f7a34 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/new_tab_url/DseNewTabUrlManagerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/new_tab_url/DseNewTabUrlManagerUnitTest.java
@@ -26,12 +26,14 @@
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.regional_capabilities.RegionalCapabilitiesServiceFactory;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
+import org.chromium.components.omnibox.OmniboxFeatureList;
 import org.chromium.components.regional_capabilities.RegionalCapabilitiesService;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.components.search_engines.TemplateUrlService;
@@ -85,6 +87,7 @@
     }
 
     @Test
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void testIsNewTabSearchEngineUrlAndroidIgnoredForNonEeaCountry() {
         assertFalse(
                 ChromeSharedPreferences.getInstance()
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java
index 2611f74..5faa6fc 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/searchwidget/SearchActivityUnitTest.java
@@ -888,7 +888,9 @@
         ShadowProfileManager.setProfile(mProfile);
         mActivity.finishNativeInitialization();
 
-        verify(urlBarCoordinator).setUrlBarHintText(R.string.hub_search_empty_hint);
+        String expectedText = mActivity.getResources().getString(R.string.hub_search_empty_hint);
+
+        verify(urlBarCoordinator).setUrlBarHintText(expectedText);
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
index 1a6c0f1..5ea5797 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
@@ -239,6 +239,41 @@
 
     @Test
     @SmallTest
+    public void testSetIsPinnedWithChange() {
+        TabStateAttributes.createForTab(mTab, TabCreationState.FROZEN_ON_RESTORE);
+        TabStateAttributes attributes = TabStateAttributes.from(mTab);
+
+        assertThat(
+                attributes.getDirtinessState(), equalTo(TabStateAttributes.DirtinessState.CLEAN));
+        assertFalse(mTab.getIsPinned());
+
+        mTab.setIsPinned(true);
+        verify(mObserver).onTabPinnedStateChanged(mTab, true);
+        assertTrue(mTab.getIsPinned());
+        assertThat(
+                attributes.getDirtinessState(), equalTo(TabStateAttributes.DirtinessState.DIRTY));
+    }
+
+    @Test
+    @SmallTest
+    public void testSetIsPinnedWithoutChange() {
+        TabStateAttributes.createForTab(mTab, TabCreationState.FROZEN_ON_RESTORE);
+        TabStateAttributes attributes = TabStateAttributes.from(mTab);
+
+        assertThat(
+                attributes.getDirtinessState(), equalTo(TabStateAttributes.DirtinessState.CLEAN));
+        assertFalse(mTab.getIsPinned());
+
+        mTab.setIsPinned(false);
+
+        verify(mObserver, never()).onTabPinnedStateChanged(any(Tab.class), anyBoolean());
+        assertFalse(mTab.getIsPinned());
+        assertThat(
+                attributes.getDirtinessState(), equalTo(TabStateAttributes.DirtinessState.CLEAN));
+    }
+
+    @Test
+    @SmallTest
     public void testFreezeDetachedNativePage() {
         TabImplJni.setInstanceForTesting(mNativeMock);
 
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index e590445..4f13291 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-138.0.7180.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-138.0.7190.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 8521c2d..a0f46d3e 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -985,9 +985,6 @@
       <message name="IDS_INSTALLER_DOWNLOADER_DISCLAIMER" desc="Installer downloader infobar message prompting users upgrading to Windows 11 to download the Chrome installer to OneDrive.">
         Upgrading to Windows 11 soon? Download the Chrome Installer to OneDrive so you’re ready from day one.
       </message>
-      <message name="IDS_INSTALLER_DOWNLOADER_LINK_TEXT" desc="Link text for the installer downloader infobar, 'Learn More'.">
-        Learn More
-      </message>
       <message name="IDS_INSTALLER_DOWNLOADER_BUTTON_LABEL" desc="Button label for the installer downloader infobar to download the Chrome installer.">
         Download Chrome Installer
       </message>
diff --git a/chrome/app/google_chrome_strings_grd/IDS_INSTALLER_DOWNLOADER_LINK_TEXT.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_INSTALLER_DOWNLOADER_LINK_TEXT.png.sha1
deleted file mode 100644
index 8488d2cf..0000000
--- a/chrome/app/google_chrome_strings_grd/IDS_INSTALLER_DOWNLOADER_LINK_TEXT.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e00968a1abcf74cc08ff5ad6f37214dcf5313930
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1e115ed..c51acce 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2997,8 +2997,6 @@
       "download/android/duplicate_download_dialog_bridge_delegate.h",
       "download/android/insecure_download_dialog_bridge.cc",
       "download/android/insecure_download_dialog_bridge.h",
-      "download/android/insecure_download_infobar_delegate.cc",
-      "download/android/insecure_download_infobar_delegate.h",
       "download/android/intercept_oma_download_navigation_throttle.cc",
       "download/android/intercept_oma_download_navigation_throttle.h",
       "download/android/items/offline_content_aggregator_factory_android.cc",
@@ -4416,6 +4414,8 @@
       "//chrome/browser/ui/webui/commerce",
       "//chrome/browser/ui/webui/commerce:impl",
       "//chrome/browser/ui/webui/cr_components/theme_color_picker",
+      "//chrome/browser/ui/webui/customize_buttons",
+      "//chrome/browser/ui/webui/customize_buttons:impl",
       "//chrome/browser/ui/webui/new_tab_footer",
       "//chrome/browser/ui/webui/new_tab_footer:impl",
       "//chrome/browser/ui/webui/privacy_sandbox",
@@ -4584,6 +4584,7 @@
       "//chrome/browser/ui:browser_list_impl",
       "//chrome/browser/importer:impl",
       "//chrome/browser/ui/webui/commerce:impl",
+      "//chrome/browser/ui/webui/customize_buttons",
       "//chrome/browser/ui/webui/settings:impl",
       "//chrome/browser/ui/webui/new_tab_footer:impl",
       "//chrome/browser/ui/webui/signin:signin_impl",
@@ -8632,6 +8633,7 @@
       "//chrome/browser/ui/tabs/tab_strip_api:mojom",
       "//chrome/browser/ui/webui/access_code_cast:mojo_bindings",
       "//chrome/browser/ui/webui/app_service_internals:mojo_bindings",
+      "//chrome/browser/ui/webui/customize_buttons:mojo_bindings",
       "//chrome/browser/ui/webui/data_sharing:mojo_bindings",
       "//chrome/browser/ui/webui/downloads:mojo_bindings",
       "//chrome/browser/ui/webui/new_tab_footer:mojo_bindings",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index a4c5852..aaa1e43 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -6553,6 +6553,11 @@
                                     kMostVisitedTilesNewScoringVariations,
                                     "MostVisitedTilesNewScoring")},
 
+    {"most-visited-tiles-visual-deduplication",
+     flag_descriptions::kMostVisitedTilesVisualDeduplicationName,
+     flag_descriptions::kMostVisitedTilesVisualDeduplicationDescription, kOsAll,
+     FEATURE_VALUE_TYPE(history::kMostVisitedTilesVisualDeduplication)},
+
     {"omnibox-local-history-zero-suggest-beyond-ntp",
      flag_descriptions::kOmniboxLocalHistoryZeroSuggestBeyondNTPName,
      flag_descriptions::kOmniboxLocalHistoryZeroSuggestBeyondNTPDescription,
@@ -8763,6 +8768,10 @@
     {"webnn-directml", flag_descriptions::kWebNNDirectMLName,
      flag_descriptions::kWebNNDirectMLDescription, kOsWin,
      FEATURE_VALUE_TYPE(webnn::mojom::features::kWebNNDirectML)},
+
+    {"webnn-onnxruntime", flag_descriptions::kWebNNOnnxRuntimeName,
+     flag_descriptions::kWebNNOnnxRuntimeDescription, kOsWin,
+     FEATURE_VALUE_TYPE(webnn::mojom::features::kWebNNOnnxRuntime)},
 #endif  // BUILDFLAG(IS_WIN)
 
     {"permission-element",
@@ -9123,6 +9132,10 @@
      flag_descriptions::kFedCmIdPRegistrationDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kFedCmIdPRegistration)},
 
+    {"fedcm-iframe-origin", flag_descriptions::kFedCmIframeOriginName,
+     flag_descriptions::kFedCmIframeOriginDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kFedCmIframeOrigin)},
+
     {"fedcm-lightweight-mode", flag_descriptions::kFedCmLightweightModeName,
      flag_descriptions::kFedCmLightweightModeDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kFedCmLightweightMode)},
diff --git a/chrome/browser/ai/ai_language_model.cc b/chrome/browser/ai/ai_language_model.cc
index f5b44470..e8a4d06d 100644
--- a/chrome/browser/ai/ai_language_model.cc
+++ b/chrome/browser/ai/ai_language_model.cc
@@ -11,6 +11,7 @@
 #include "base/check_op.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/notimplemented.h"
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
@@ -155,6 +156,7 @@
   void AppendAndGenerate(
       mojo::PendingRemote<on_device_model::mojom::Session> session,
       base::OnceClosure callback) {
+    start_ = base::TimeTicks::Now();
     callback_ = std::move(callback);
     safety_checker_->RunRequestChecks(
         CreateStringMessage(*input_),
@@ -211,6 +213,11 @@
 
   // on_device_model::mojom::ContextClient:
   void OnComplete(uint32_t tokens_processed) override {
+    base::UmaHistogramCounts10000("AI.Session.LanguageModel.ContextTokens",
+                                  tokens_processed);
+    base::UmaHistogramMediumTimes("AI.Session.LanguageModel.ContextTime",
+                                  base::TimeTicks::Now() - start_);
+    generate_start_ = base::TimeTicks::Now();
     context_receiver_.reset();
     token_count_ = tokens_processed;
     if (mode_ == Mode::kAppendOnly) {
@@ -221,6 +228,11 @@
 
   // on_device_model::mojom::StreamingResponder:
   void OnResponse(on_device_model::mojom::ResponseChunkPtr chunk) override {
+    if (full_response_.empty()) {
+      base::UmaHistogramMediumTimes(
+          "AI.Session.LanguageModel.FirstResponseTime",
+          base::TimeTicks::Now() - start_);
+    }
     output_tokens_++;
     full_response_ += chunk->text;
 
@@ -292,6 +304,11 @@
       return;
     }
     token_count_ += summary->output_token_count;
+    base::UmaHistogramMediumTimes(
+        "AI.Session.LanguageModel.ResponseCompleteTime",
+        base::TimeTicks::Now() - generate_start_);
+    base::UmaHistogramCounts10000("AI.Session.LanguageModel.ResponseTokens",
+                                  summary->output_token_count);
     std::move(callback_).Run();
     // `this` may be deleted.
   }
@@ -341,6 +358,9 @@
 
   Mode mode_;
 
+  base::TimeTicks start_;
+  base::TimeTicks generate_start_;
+
   base::WeakPtrFactory<PromptState> weak_factory_{this};
 };
 
@@ -424,7 +444,11 @@
       optimization_guide::SafetyConfig(model_client_->safety_config()));
 }
 
-AILanguageModel::~AILanguageModel() = default;
+AILanguageModel::~AILanguageModel() {
+  // If the initial session has been reset, the session crashed.
+  base::UmaHistogramBoolean("AI.Session.LanguageModel.Crashed",
+                            !initial_session_);
+}
 
 // static
 PromptApiMetadata AILanguageModel::ParseMetadata(
diff --git a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
index 41f2274..a58b7951 100644
--- a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
+++ b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/BrowserServicesIntentDataProvider.java
@@ -767,4 +767,12 @@
     public @OpenInBrowserState int getOpenInBrowserButtonState() {
         return OPEN_IN_BROWSER_STATE_OFF;
     }
+
+    /**
+     * @return the TWA startup timestamp associated with an intent in the uptimeMillis timebase, or
+     *     null.
+     */
+    public @Nullable Long getTwaStartupUptimeMillis() {
+        return null;
+    }
 }
diff --git a/chrome/browser/android/preloading/android_prerender_manager.cc b/chrome/browser/android/preloading/android_prerender_manager.cc
index 701a9dd..6b36205 100644
--- a/chrome/browser/android/preloading/android_prerender_manager.cc
+++ b/chrome/browser/android/preloading/android_prerender_manager.cc
@@ -41,20 +41,28 @@
   PrerenderManager::CreateForWebContents(web_contents);
   auto* prerender_manager = PrerenderManager::FromWebContents(web_contents);
   CHECK(prerender_manager);
-  prerender_handle_ = prerender_manager->StartPrerenderNewTabPage(
-      prerender_url, chrome_preloading_predictor::kTouchOnNewTabPage);
-  return prerender_handle_ != nullptr;
+  base::WeakPtr<content::PrerenderHandle> prerender_handle =
+      prerender_manager->StartPrerenderNewTabPage(
+          prerender_url, chrome_preloading_predictor::kTouchOnNewTabPage);
+  if (prerender_handle) {
+    prerender_handle_map_[web_contents] = prerender_handle;
+  } else if (prerender_handle_map_.contains(web_contents) &&
+             !prerender_handle_map_[web_contents]) {
+    prerender_handle_map_.erase(web_contents);
+  }
+  return prerender_handle != nullptr;
 }
 
 void AndroidPrerenderManager::StopPrerendering(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& j_web_contents) {
-  if (prerender_handle_) {
-    content::WebContents* const web_contents =
-        content::WebContents::FromJavaWebContents(j_web_contents);
+  content::WebContents* const web_contents =
+      content::WebContents::FromJavaWebContents(j_web_contents);
+  if (prerender_handle_map_.contains(web_contents)) {
     auto* prerender_manager = PrerenderManager::FromWebContents(web_contents);
     CHECK(prerender_manager);
-    prerender_manager->StopPrerenderNewTabPage(prerender_handle_);
-    prerender_handle_ = nullptr;
+    prerender_manager->StopPrerenderNewTabPage(
+        prerender_handle_map_[web_contents]);
+    prerender_handle_map_.erase(web_contents);
   }
 }
diff --git a/chrome/browser/android/preloading/android_prerender_manager.h b/chrome/browser/android/preloading/android_prerender_manager.h
index 0973761..d78e724 100644
--- a/chrome/browser/android/preloading/android_prerender_manager.h
+++ b/chrome/browser/android/preloading/android_prerender_manager.h
@@ -6,9 +6,12 @@
 #define CHROME_BROWSER_ANDROID_PRELOADING_ANDROID_PRERENDER_MANAGER_H_
 
 #include "base/android/jni_weak_ref.h"
+#include "base/containers/flat_map.h"
 #include "chrome/browser/preloading/prerender/prerender_manager.h"
 #include "url/android/gurl_android.h"
 
+// This object is owned through a Java-side singletone object, and the object
+// can be shared by multiple tabs (WebContents).
 class AndroidPrerenderManager {
  public:
   AndroidPrerenderManager(JNIEnv* env, jobject obj);
@@ -28,7 +31,9 @@
       const base::android::JavaParamRef<jobject>& j_web_contents);
 
  private:
-  base::WeakPtr<content::PrerenderHandle> prerender_handle_;
+  base::flat_map<raw_ptr<content::WebContents>,
+                 base::WeakPtr<content::PrerenderHandle>>
+      prerender_handle_map_;
 };
 
 #endif  // CHROME_BROWSER_ANDROID_PRELOADING_ANDROID_PRERENDER_MANAGER_H_
diff --git a/chrome/browser/apps/app_service/web_contents_app_id_utils.cc b/chrome/browser/apps/app_service/web_contents_app_id_utils.cc
index 5c9ba4b..1d4bed9 100644
--- a/chrome/browser/apps/app_service/web_contents_app_id_utils.cc
+++ b/chrome/browser/apps/app_service/web_contents_app_id_utils.cc
@@ -7,7 +7,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
-#include "chrome/browser/extensions/tab_helper.h"
+#include "chrome/browser/extensions/app_tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
@@ -44,11 +44,12 @@
     return *app_id;
   }
 
-  extensions::TabHelper* extensions_tab_helper =
-      extensions::TabHelper::FromWebContents(web_contents);
-  // extensions_tab_helper is nullptr in some tests.
-  return extensions_tab_helper ? extensions_tab_helper->GetExtensionAppId()
-                               : std::string();
+  extensions::AppTabHelper* extensions_app_tab_helper =
+      extensions::AppTabHelper::FromWebContents(web_contents);
+  // extensions_app_tab_helper is nullptr in some tests.
+  return extensions_app_tab_helper
+             ? extensions_app_tab_helper->GetExtensionAppId()
+             : std::string();
 }
 
 void SetAppIdForWebContents(Profile* profile,
@@ -57,10 +58,11 @@
   if (!web_app::AreWebAppsEnabled(profile)) {
     return;
   }
-  auto* extension_helper = extensions::TabHelper::FromWebContents(web_contents);
+  auto* extensions_app_tab_helper =
+      extensions::AppTabHelper::FromWebContents(web_contents);
   auto* web_app_helper =
       web_app::WebAppTabHelper::FromWebContents(web_contents);
-  if (!extension_helper || !web_app_helper) {
+  if (!extensions_app_tab_helper || !web_app_helper) {
     return;
   }
 
@@ -70,12 +72,12 @@
   if (extension) {
     DCHECK(extension->is_app());
     web_app_helper->SetAppId(std::nullopt);
-    extension_helper->SetExtensionAppById(app_id);
+    extensions_app_tab_helper->SetExtensionAppById(app_id);
   } else {
     bool app_installed = IsAppReady(profile, app_id);
     web_app_helper->SetAppId(
         app_installed ? std::optional<webapps::AppId>(app_id) : std::nullopt);
-    extension_helper->SetExtensionAppById(std::string());
+    extensions_app_tab_helper->SetExtensionAppById(std::string());
   }
 }
 
diff --git a/chrome/browser/apps/digital_goods/util.cc b/chrome/browser/apps/digital_goods/util.cc
index d43a5d7..f2ce165a2 100644
--- a/chrome/browser/apps/digital_goods/util.cc
+++ b/chrome/browser/apps/digital_goods/util.cc
@@ -5,14 +5,15 @@
 #include "chrome/browser/apps/digital_goods/util.h"
 
 #include <optional>
+#include <string>
 
 #include "chrome/browser/ash/apps/apk_web_app_service.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/web_applications/proto/web_app_install_state.pb.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
+#include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/document_user_data.h"
 #include "content/public/browser/web_contents.h"
 
@@ -24,18 +25,16 @@
   if (!web_contents)
     return std::string();
 
-  Browser* browser = chrome::FindBrowserWithTab(web_contents);
-  if (!web_app::AppBrowserController::IsWebApp(browser)) {
+  tabs::TabInterface* tab = tabs::TabInterface::GetFromContents(web_contents);
+  BrowserWindowInterface* browser = tab->GetBrowserWindowInterface();
+  if (!browser || !browser->GetProfile()) {
+    return std::string();
+  }
+  if (browser->GetProfile()->IsIncognitoProfile()) {
     return std::string();
   }
 
-  auto* profile =
-      Profile::FromBrowserContext(render_frame_host->GetBrowserContext());
-  if (profile->IsIncognitoProfile()) {
-    return std::string();
-  }
-
-  auto* apk_web_app_service = ash::ApkWebAppService::Get(profile);
+  auto* apk_web_app_service = ash::ApkWebAppService::Get(browser->GetProfile());
   if (!apk_web_app_service) {
     return std::string();
   }
diff --git a/chrome/browser/ash/boca/BUILD.gn b/chrome/browser/ash/boca/BUILD.gn
index a1cae35..b095ce6 100644
--- a/chrome/browser/ash/boca/BUILD.gn
+++ b/chrome/browser/ash/boca/BUILD.gn
@@ -16,7 +16,10 @@
     "boca_manager_factory.h",
   ]
 
-  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+  public_deps = [
+    "//chrome/browser:browser_public_dependencies",
+    "//chrome/browser/ui:browser_list",
+  ]
 
   deps = [
     "//ash/constants",
diff --git a/chrome/browser/ash/boca/boca_app_client_impl.cc b/chrome/browser/ash/boca/boca_app_client_impl.cc
index e3ea991..decadad 100644
--- a/chrome/browser/ash/boca/boca_app_client_impl.cc
+++ b/chrome/browser/ash/boca/boca_app_client_impl.cc
@@ -11,6 +11,8 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace ash::boca {
@@ -49,6 +51,14 @@
                                SystemWebAppType::BOCA);
 }
 
+bool BocaAppClientImpl::HasApp() {
+  auto* const browser = ash::FindSystemWebAppBrowser(
+      ProfileManager::GetActiveUserProfile(), SystemWebAppType::BOCA);
+  return browser &&
+         !BrowserList::GetInstance()->currently_closing_browsers().contains(
+             browser);
+}
+
 void BocaAppClientImpl::OpenFeedbackDialog() {
   Profile* profile = ProfileManager::GetActiveUserProfile();
   constexpr char kBocaAppFeedbackCategoryTag[] = "Boca";
diff --git a/chrome/browser/ash/boca/boca_app_client_impl.h b/chrome/browser/ash/boca/boca_app_client_impl.h
index 4d0ff0d..67e0be4 100644
--- a/chrome/browser/ash/boca/boca_app_client_impl.h
+++ b/chrome/browser/ash/boca/boca_app_client_impl.h
@@ -29,6 +29,7 @@
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   std::string GetDeviceId() override;
   void LaunchApp() override;
+  bool HasApp() override;
   void OpenFeedbackDialog() override;
 };
 }  // namespace ash::boca
diff --git a/chrome/browser/ash/input_method/get_current_window_properties.cc b/chrome/browser/ash/input_method/get_current_window_properties.cc
index d1e85ac9..caf44ac 100644
--- a/chrome/browser/ash/input_method/get_current_window_properties.cc
+++ b/chrome/browser/ash/input_method/get_current_window_properties.cc
@@ -20,11 +20,7 @@
 
 std::optional<GURL> GetFocusedTabUrl() {
   Browser* browser = chrome::FindLastActive();
-  // Ash chrome will return true for browser->window()->IsActive() if the
-  // user is currently typing in an ash browser tab. IsActive() will return
-  // false if the user is currently typing a lacros browser tab.
-  if (browser && browser->window() && browser->window()->IsActive() &&
-      browser->tab_strip_model() &&
+  if (browser && browser->window() && browser->tab_strip_model() &&
       browser->tab_strip_model()->GetActiveWebContents()) {
     return browser->tab_strip_model()
         ->GetActiveWebContents()
diff --git a/chrome/browser/ash/login/test/cryptohome_mixin.h b/chrome/browser/ash/login/test/cryptohome_mixin.h
index e42a6f80..f453c7ae 100644
--- a/chrome/browser/ash/login/test/cryptohome_mixin.h
+++ b/chrome/browser/ash/login/test/cryptohome_mixin.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_BROWSER_ASH_LOGIN_TEST_CRYPTOHOME_MIXIN_H_
 #define CHROME_BROWSER_ASH_LOGIN_TEST_CRYPTOHOME_MIXIN_H_
 
-#include <queue>
 #include <string>
 #include <utility>
 
diff --git a/chrome/browser/ash/policy/fuzzer/BUILD.gn b/chrome/browser/ash/policy/fuzzer/BUILD.gn
index 463d37a..8aaa857d 100644
--- a/chrome/browser/ash/policy/fuzzer/BUILD.gn
+++ b/chrome/browser/ash/policy/fuzzer/BUILD.gn
@@ -62,8 +62,9 @@
       "$root_gen_dir/components/policy/proto",
     ]
 
-    proto_deps = [ "//components/policy/proto/fuzzer" ]
-
-    link_deps = [ "//components/policy/proto/fuzzer" ]
+    proto_library_deps = [
+      "//components/policy/proto/fuzzer:chrome_device_policy_full_runtime_proto",
+      "//components/policy/proto/fuzzer:cloud_policy_full_runtime_proto",
+    ]
   }
 }
diff --git a/chrome/browser/ash/system_web_apps/apps/boca_app_integration_browsertest.cc b/chrome/browser/ash/system_web_apps/apps/boca_app_integration_browsertest.cc
index efa134f..8407f46 100644
--- a/chrome/browser/ash/system_web_apps/apps/boca_app_integration_browsertest.cc
+++ b/chrome/browser/ash/system_web_apps/apps/boca_app_integration_browsertest.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/ash/components/boca/boca_session_manager.h"
 #include "chromeos/ash/components/boca/proto/roster.pb.h"
 #include "chromeos/ash/components/boca/proto/session.pb.h"
@@ -144,7 +145,7 @@
 }
 
 IN_PROC_BROWSER_TEST_P(BocaAppProviderIntegrationTest,
-                       ShouldEndSessionWhenAppClose) {
+                       ShouldEndSessionWhenLastAppWindowClose) {
   LaunchAndWait();
   base::test::TestFuture<void> future;
   boca_session_manager()->set_end_session_callback_for_testing(
@@ -153,6 +154,27 @@
       ash::FindSystemWebAppBrowser(profile(), ash::SystemWebAppType::BOCA);
   boca_app_browser->window()->Close();
   EXPECT_TRUE(future.Wait());
+  EXPECT_FALSE(boca_session_manager()->end_session_callback_for_testing());
+}
+
+IN_PROC_BROWSER_TEST_P(BocaAppProviderIntegrationTest,
+                       ShouldNotEndSessionWhenStillAppWindowOpen) {
+  LaunchAndWait();
+
+  base::test::TestFuture<void> future;
+  boca_session_manager()->set_end_session_callback_for_testing(
+      future.GetCallback());
+  Browser* const boca_app_browser =
+      ash::FindSystemWebAppBrowser(profile(), ash::SystemWebAppType::BOCA);
+
+  // Trigger reload which will cause page handler to be recreated.
+  ui_test_utils::NavigateToURLWithDisposition(
+      boca_app_browser, GURL(ash::boca::kChromeBocaAppUntrustedIndexURL),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
+
+  // Callback never executed.
+  EXPECT_TRUE(boca_session_manager()->end_session_callback_for_testing());
 }
 
 IN_PROC_BROWSER_TEST_P(BocaAppProviderIntegrationTest,
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index 05f543f..c468ae41 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -550,12 +550,14 @@
 #if !BUILDFLAG(IS_ANDROID)
   if (auto* lens_search_controller =
           GetLensSearchController(GetWebContents(web_contents_getter_))) {
-    // Guaranteed to exist if lens_search_controller is not null.
+    // Only allow Lens entrypoints if the Lens overlay is enabled and Lens is
+    // not currently active. Guaranteed to exist if lens_search_controller is
+    // not null.
     return lens_search_controller->GetTabInterface()
         ->GetBrowserWindowInterface()
         ->GetFeatures()
         .lens_overlay_entry_point_controller()
-        ->IsEnabled();
+        ->AreVisible();
   }
 #endif
   return false;
diff --git a/chrome/browser/chrome_browser_interface_binders_webui.cc b/chrome/browser/chrome_browser_interface_binders_webui.cc
index d12f445f0..88115ef 100644
--- a/chrome/browser/chrome_browser_interface_binders_webui.cc
+++ b/chrome/browser/chrome_browser_interface_binders_webui.cc
@@ -90,6 +90,7 @@
 #include "chrome/browser/ui/webui/app_service_internals/app_service_internals_ui.h"
 #include "chrome/browser/ui/webui/commerce/product_specifications_ui.h"
 #include "chrome/browser/ui/webui/commerce/shopping_insights_side_panel_ui.h"
+#include "chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom.h"
 #include "chrome/browser/ui/webui/data_sharing/data_sharing.mojom.h"
 #include "chrome/browser/ui/webui/data_sharing/data_sharing_ui.h"
 #include "chrome/browser/ui/webui/downloads/downloads.mojom.h"
@@ -528,6 +529,10 @@
       NewTabFooterUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
+      customize_buttons::mojom::CustomizeButtonsHandlerFactory, NewTabPageUI>(
+      map);
+
+  RegisterWebUIControllerInterfaceBinder<
       new_tab_page::mojom::PageHandlerFactory, NewTabPageUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
@@ -1221,7 +1226,7 @@
   }
 
   RegisterWebUIControllerInterfaceBinder<
-      privacy_sandbox::dialog::mojom::BaseDialogPageHandler,
+      privacy_sandbox::dialog::mojom::BaseDialogPageHandlerFactory,
       privacy_sandbox::BaseDialogUI>(map);
 #endif
 
diff --git a/chrome/browser/collaboration/BUILD.gn b/chrome/browser/collaboration/BUILD.gn
index 991b0f7..b943cef 100644
--- a/chrome/browser/collaboration/BUILD.gn
+++ b/chrome/browser/collaboration/BUILD.gn
@@ -19,6 +19,7 @@
       ":java_resources",
       "//base:base_java",
       "//chrome/browser/data_sharing:data_sharing_public_java",
+      "//chrome/browser/feature_engagement:java",
       "//chrome/browser/profiles/android:java",
       "//chrome/browser/settings:factory_java",
       "//chrome/browser/signin/services/android:java",
@@ -32,6 +33,7 @@
       "//components/collaboration/public:java",
       "//components/data_sharing/public:public_java",
       "//components/data_sharing/public/protocol:proto_java",
+      "//components/feature_engagement/public:public_java",
       "//components/saved_tab_groups/public:java",
       "//components/signin/public/android:java",
       "//components/strings:components_strings_grd",
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
index 97d6b5a..9e66743 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
@@ -19,6 +19,7 @@
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.data_sharing.DataSharingMetrics;
 import org.chromium.chrome.browser.data_sharing.DataSharingTabManager;
+import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.SettingsNavigationFactory;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
@@ -45,6 +46,7 @@
 import org.chromium.components.data_sharing.SharedTabGroupPreview;
 import org.chromium.components.data_sharing.configs.DataSharingCreateUiConfig;
 import org.chromium.components.data_sharing.configs.DataSharingJoinUiConfig;
+import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
 import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
@@ -77,6 +79,9 @@
     // Stores the runnable to close the current showing UI. Is null when there's no UI showing.
     private @Nullable Runnable mCloseScreenRunnable;
 
+    /** Used to suppress IPH UIs while a collaboration flow UI is on the screen. */
+    private Tracker.@Nullable DisplayLockHandle mFeatureEngagementLock;
+
     /**
      * Constructor for a new {@link CollaborationControllerDelegateImpl} object.
      *
@@ -125,6 +130,11 @@
     @CalledByNative
     void prepareFlowUI(long exitCallback, long resultCallback) {
         mExitCallback = exitCallback;
+
+        // Acquire lock to prevent IPH from being shown in a collaboration flow.
+        Tracker tracker = TrackerFactory.getTrackerForProfile(mDataSharingTabManager.getProfile());
+        mFeatureEngagementLock = tracker.acquireDisplayLock();
+
         Runnable onTabSwitcherShownRunnable =
                 () -> {
                     CollaborationControllerDelegateImplJni.get()
@@ -683,6 +693,9 @@
         mDataSharingTabManager.onCollaborationDelegateFlowFinished();
         cleanUpPointers();
 
+        if (mFeatureEngagementLock != null) {
+            mFeatureEngagementLock.release();
+        }
         if (mExitCallback != 0) {
             CollaborationControllerDelegateImplJni.get().deleteExitCallback(mExitCallback);
         }
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
index 7926e7b..f91351a 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
@@ -31,6 +31,7 @@
 import org.chromium.base.Token;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.data_sharing.DataSharingTabManager;
+import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.SettingsNavigationFactory;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
@@ -50,6 +51,7 @@
 import org.chromium.components.data_sharing.SharedTabGroupPreview;
 import org.chromium.components.data_sharing.configs.DataSharingCreateUiConfig;
 import org.chromium.components.data_sharing.configs.DataSharingJoinUiConfig;
+import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
 import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
@@ -87,6 +89,7 @@
     @Mock private Callback<Runnable> mSwitchToTabSwitcherCallback;
     @Mock private Callback<Callback<Boolean>> mStartAccountRefreshCallback;
     @Mock private CollaborationService mCollaborationService;
+    @Mock private Tracker mMockTracker;
 
     @Mock
     private CollaborationControllerDelegateImpl.Natives
@@ -103,6 +106,7 @@
         SettingsNavigationFactory.setInstanceForTesting(mSettingsNavigation);
         IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
         CollaborationServiceFactory.setForTesting(mCollaborationService);
+        TrackerFactory.setTrackerForTests(mMockTracker);
 
         doReturn(
                         new ServiceStatus(
diff --git a/chrome/browser/data_sharing/android/java/res/values/dimens.xml b/chrome/browser/data_sharing/android/java/res/values/dimens.xml
index a8d48c0..b25c25c4 100644
--- a/chrome/browser/data_sharing/android/java/res/values/dimens.xml
+++ b/chrome/browser/data_sharing/android/java/res/values/dimens.xml
@@ -31,4 +31,6 @@
     <dimen name="recent_activity_favicon_bg_size">40dp</dimen>
     <dimen name="recent_activity_menu_width">258dp</dimen>
 
+    <!-- Maximum favicon size for DataSharingUiDelegate -->
+    <dimen name="shared_tab_group_favicon_bitmap_size">34dp</dimen>
 </resources>
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
index f6f6ce3..470e959 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
@@ -399,8 +399,8 @@
                 activity,
                 assumeNonNull(mProfile),
                 urls,
-                // TODO(haileywang): add this to resources when using it in service.
-                /* size= */ 72,
+                activity.getResources()
+                        .getDimensionPixelSize(R.dimen.shared_tab_group_favicon_bitmap_size),
                 (favicons) -> {
                     updateFavicons(sessionId, displayUrls, favicons);
                     doneCallback.run();
diff --git a/chrome/browser/devtools/features.cc b/chrome/browser/devtools/features.cc
index 81c050a..550c310d 100644
--- a/chrome/browser/devtools/features.cc
+++ b/chrome/browser/devtools/features.cc
@@ -152,7 +152,7 @@
 // Whether DevTools will offer the new CSS value tracing UI.
 BASE_FEATURE(kDevToolsCssValueTracing,
              "DevToolsCssValueTracing",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Whether the DevTools AI generated annotation labels in timeline are enabled.
 BASE_FEATURE(kDevToolsAiGeneratedTimelineLabels,
diff --git a/chrome/browser/download/android/insecure_download_infobar_delegate.cc b/chrome/browser/download/android/insecure_download_infobar_delegate.cc
deleted file mode 100644
index 22893f35..0000000
--- a/chrome/browser/download/android/insecure_download_infobar_delegate.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/download/android/insecure_download_infobar_delegate.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/memory/ptr_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task/single_thread_task_runner.h"
-#include "chrome/browser/android/android_theme_resources.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/download/public/common/download_item.h"
-#include "components/infobars/android/confirm_infobar.h"
-#include "components/infobars/content/content_infobar_manager.h"
-#include "components/infobars/core/infobar.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/strings/grit/ui_strings.h"
-
-using InsecureDownloadStatus = download::DownloadItem::InsecureDownloadStatus;
-
-// static
-void InsecureDownloadInfoBarDelegate::Create(
-    infobars::ContentInfoBarManager* infobar_manager,
-    const base::FilePath& basename,
-    download::DownloadItem::InsecureDownloadStatus insecure_download_status,
-    ResultCallback callback) {
-  infobar_manager->AddInfoBar(std::make_unique<infobars::ConfirmInfoBar>(
-      base::WrapUnique(new InsecureDownloadInfoBarDelegate(
-          basename, insecure_download_status, std::move(callback)))));
-}
-
-InsecureDownloadInfoBarDelegate::InsecureDownloadInfoBarDelegate(
-    const base::FilePath& basename,
-    download::DownloadItem::InsecureDownloadStatus insecure_download_status,
-    ResultCallback callback)
-    : insecure_download_status_(insecure_download_status),
-      callback_(std::move(callback)) {
-  message_text_ =
-      l10n_util::GetStringFUTF16(IDS_PROMPT_CONFIRM_INSECURE_DOWNLOAD,
-                                 base::UTF8ToUTF16(basename.value()));
-}
-
-InsecureDownloadInfoBarDelegate::~InsecureDownloadInfoBarDelegate() = default;
-
-infobars::InfoBarDelegate::InfoBarIdentifier
-InsecureDownloadInfoBarDelegate::GetIdentifier() const {
-  return INSECURE_DOWNLOAD_INFOBAR_DELEGATE_ANDROID;
-}
-
-int InsecureDownloadInfoBarDelegate::GetIconId() const {
-  return IDR_ANDROID_INFOBAR_WARNING;
-}
-
-bool InsecureDownloadInfoBarDelegate::ShouldExpire(
-    const NavigationDetails& details) const {
-  return false;
-}
-
-void InsecureDownloadInfoBarDelegate::InfoBarDismissed() {
-  PostReply(false);
-}
-
-std::u16string InsecureDownloadInfoBarDelegate::GetMessageText() const {
-  return message_text_;
-}
-
-std::u16string InsecureDownloadInfoBarDelegate::GetButtonLabel(
-    InfoBarButton button) const {
-  if (insecure_download_status_ == InsecureDownloadStatus::WARN) {
-    return l10n_util::GetStringUTF16(
-        button == BUTTON_OK ? IDS_CONFIRM_DOWNLOAD : IDS_DISCARD_DOWNLOAD);
-  }
-
-  DCHECK_EQ(insecure_download_status_, InsecureDownloadStatus::BLOCK);
-  // Default button is Discard when blocking.
-  return l10n_util::GetStringUTF16(button == BUTTON_OK ? IDS_DISCARD_DOWNLOAD
-                                                       : IDS_CONFIRM_DOWNLOAD);
-}
-
-bool InsecureDownloadInfoBarDelegate::Accept() {
-  if (insecure_download_status_ == InsecureDownloadStatus::WARN) {
-    PostReply(true);
-    return true;
-  }
-
-  DCHECK_EQ(insecure_download_status_, InsecureDownloadStatus::BLOCK);
-  // Default button is Discard when blocking.
-  PostReply(false);
-  return true;
-}
-
-bool InsecureDownloadInfoBarDelegate::Cancel() {
-  if (insecure_download_status_ == InsecureDownloadStatus::WARN) {
-    PostReply(false);
-    return true;
-  }
-
-  CHECK_EQ(insecure_download_status_, InsecureDownloadStatus::BLOCK);
-  // Cancel button is Keep when blocking.
-  PostReply(true);
-  return true;
-}
-
-void InsecureDownloadInfoBarDelegate::PostReply(bool should_download) {
-  DCHECK(callback_);
-  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback_), should_download));
-}
diff --git a/chrome/browser/download/android/insecure_download_infobar_delegate.h b/chrome/browser/download/android/insecure_download_infobar_delegate.h
deleted file mode 100644
index 2c85d7d..0000000
--- a/chrome/browser/download/android/insecure_download_infobar_delegate.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_DOWNLOAD_ANDROID_INSECURE_DOWNLOAD_INFOBAR_DELEGATE_H_
-#define CHROME_BROWSER_DOWNLOAD_ANDROID_INSECURE_DOWNLOAD_INFOBAR_DELEGATE_H_
-
-#include "base/files/file_path.h"
-#include "base/functional/callback.h"
-#include "components/download/public/common/download_item.h"
-#include "components/infobars/core/confirm_infobar_delegate.h"
-
-namespace infobars {
-class ContentInfoBarManager;
-}
-
-// An infobar that asks if user wants to download an insecurely delivered file.
-// Note that this infobar does not expire if the user subsequently navigates,
-// since such navigations won't automatically cancel the underlying download.
-class InsecureDownloadInfoBarDelegate : public ConfirmInfoBarDelegate {
- public:
-  using ResultCallback = base::OnceCallback<void(bool should_download)>;
-
-  static void Create(
-      infobars::ContentInfoBarManager* infobar_manager,
-      const base::FilePath& basename,
-      download::DownloadItem::InsecureDownloadStatus insecure_download_status,
-      ResultCallback callback);
-
-  InsecureDownloadInfoBarDelegate(const InsecureDownloadInfoBarDelegate&) =
-      delete;
-  InsecureDownloadInfoBarDelegate& operator=(
-      const InsecureDownloadInfoBarDelegate&) = delete;
-
-  ~InsecureDownloadInfoBarDelegate() override;
-
- private:
-  explicit InsecureDownloadInfoBarDelegate(
-      const base::FilePath& basename,
-      download::DownloadItem::InsecureDownloadStatus insecure_download_status,
-      ResultCallback callback);
-
-  // ConfirmInfoBarDelegate:
-  infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
-  int GetIconId() const override;
-  bool ShouldExpire(const NavigationDetails& details) const override;
-  void InfoBarDismissed() override;
-  std::u16string GetMessageText() const override;
-  std::u16string GetButtonLabel(InfoBarButton button) const override;
-  bool Accept() override;
-  bool Cancel() override;
-
-  // Calls callback_ with the appropriate result.
-  void PostReply(bool should_download);
-
-  std::u16string message_text_;
-  download::DownloadItem::InsecureDownloadStatus insecure_download_status_;
-  ResultCallback callback_;
-};
-
-#endif  // CHROME_BROWSER_DOWNLOAD_ANDROID_INSECURE_DOWNLOAD_INFOBAR_DELEGATE_H_
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index a463625..3fedb04c 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -109,7 +109,6 @@
 #include "chrome/browser/download/android/download_utils.h"
 #include "chrome/browser/download/android/duplicate_download_dialog_bridge_delegate.h"
 #include "chrome/browser/download/android/insecure_download_dialog_bridge.h"
-#include "chrome/browser/download/android/insecure_download_infobar_delegate.h"
 #include "chrome/browser/download/android/new_navigation_observer.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
 #include "chrome/browser/ui/android/pdf/pdf_jni_headers/PdfUtils_jni.h"
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 5138533..1db684b 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -354,6 +354,8 @@
     "permissions/permissions_updater.h",
     "permissions/scripting_permissions_modifier.cc",
     "permissions/scripting_permissions_modifier.h",
+    "permissions/site_permissions_helper.cc",
+    "permissions/site_permissions_helper.h",
     "permissions_based_management_policy_provider.cc",
     "permissions_based_management_policy_provider.h",
     "permissions_url_constants.cc",
@@ -382,6 +384,8 @@
     "startup_helper.cc",
     "startup_helper.h",
     "system_display/display_info_provider.h",
+    "tab_helper.cc",
+    "tab_helper.h",
     "unpacked_installer.cc",
     "unpacked_installer.h",
     "updater/chrome_extension_downloader_factory.cc",
@@ -430,6 +434,7 @@
     "//chrome/app:command_ids",
     "//chrome/app/theme:chrome_unscaled_resources",
     "//chrome/browser:browser_process",
+    "//chrome/browser:shell_integration",
     "//chrome/browser/apps:user_type_filter",
     "//chrome/browser/bitmap_fetcher",
     "//chrome/browser/content_settings:content_settings_factory",
@@ -672,6 +677,8 @@
       "api/tabs/tabs_windows_api.h",
       "api/tabs/windows_event_router.cc",
       "api/tabs/windows_event_router.h",
+      "app_tab_helper.cc",
+      "app_tab_helper.h",
       "browser_extension_window_controller.cc",
       "browser_extension_window_controller.h",
       "chrome_app_icon_loader.cc",
@@ -746,12 +753,8 @@
       "mv2_deprecation_impact_checker.h",
       "navigation_extension_enabler.cc",
       "navigation_extension_enabler.h",
-      "permissions/site_permissions_helper.cc",
-      "permissions/site_permissions_helper.h",
       "sync_bundle.cc",
       "sync_bundle.h",
-      "tab_helper.cc",
-      "tab_helper.h",
       "theme_installed_infobar_delegate.cc",
       "theme_installed_infobar_delegate.h",
       "warning_badge_service.cc",
@@ -847,7 +850,6 @@
       "//chrome/app/vector_icons",
       "//chrome/browser:font_pref",
       "//chrome/browser:primitives",
-      "//chrome/browser:shell_integration",
       "//chrome/browser/accessibility/tree_fixing:prefs",
       "//chrome/browser/affiliations",
       "//chrome/browser/apps:user_type_filter",
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.cc b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
index e4a9379..48243ec 100644
--- a/chrome/browser/extensions/api/chrome_extensions_api_client.cc
+++ b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
@@ -17,6 +17,8 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.h"
+#include "chrome/browser/extensions/api/declarative_content/chrome_content_rules_registry.h"
+#include "chrome/browser/extensions/api/declarative_content/default_content_predicate_evaluators.h"
 #include "chrome/browser/extensions/api/management/chrome_management_api_delegate.h"
 #include "chrome/browser/extensions/api/metrics_private/chrome_metrics_private_delegate.h"
 #include "chrome/browser/extensions/api/storage/managed_value_store_cache.h"
@@ -212,6 +214,16 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+scoped_refptr<ContentRulesRegistry>
+ChromeExtensionsAPIClient::CreateContentRulesRegistry(
+    content::BrowserContext* browser_context,
+    RulesCacheDelegate* cache_delegate) const {
+  return base::MakeRefCounted<ChromeContentRulesRegistry>(
+      browser_context, cache_delegate,
+      base::BindOnce(&CreateDefaultContentPredicateEvaluators,
+                     base::Unretained(browser_context)));
+}
+
 #if BUILDFLAG(IS_CHROMEOS)
 bool ChromeExtensionsAPIClient::ShouldAllowDetachingUsb(int vid,
                                                         int pid) const {
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client_android.cc b/chrome/browser/extensions/api/chrome_extensions_api_client_android.cc
index df8fb60..1848d6c 100644
--- a/chrome/browser/extensions/api/chrome_extensions_api_client_android.cc
+++ b/chrome/browser/extensions/api/chrome_extensions_api_client_android.cc
@@ -54,16 +54,6 @@
   NOTIMPLEMENTED();
 }
 
-scoped_refptr<ContentRulesRegistry>
-ChromeExtensionsAPIClient::CreateContentRulesRegistry(
-    content::BrowserContext* browser_context,
-    RulesCacheDelegate* cache_delegate) const {
-  // TODO(crbug.com/417786079): Support ChromeContentRulesRegistry on desktop
-  // Android.
-  NOTIMPLEMENTED();
-  return nullptr;
-}
-
 std::unique_ptr<DevicePermissionsPrompt>
 ChromeExtensionsAPIClient::CreateDevicePermissionsPrompt(
     content::WebContents* web_contents) const {
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client_non_android.cc b/chrome/browser/extensions/api/chrome_extensions_api_client_non_android.cc
index 7055105..986cdd6 100644
--- a/chrome/browser/extensions/api/chrome_extensions_api_client_non_android.cc
+++ b/chrome/browser/extensions/api/chrome_extensions_api_client_non_android.cc
@@ -7,8 +7,6 @@
 
 #include "chrome/browser/extensions/api/chrome_device_permissions_prompt.h"
 #include "chrome/browser/extensions/api/chrome_extensions_api_client.h"
-#include "chrome/browser/extensions/api/declarative_content/chrome_content_rules_registry.h"
-#include "chrome/browser/extensions/api/declarative_content/default_content_predicate_evaluators.h"
 #include "chrome/browser/extensions/api/messaging/chrome_messaging_delegate.h"
 #include "chrome/browser/extensions/api/messaging/chrome_native_message_port_dispatcher.h"
 #include "chrome/browser/extensions/extension_action_dispatcher.h"
@@ -22,6 +20,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/api/system_display/display_info_provider.h"
 #include "extensions/browser/extension_action.h"
@@ -159,16 +158,6 @@
   Navigate(&navigate_params);
 }
 
-scoped_refptr<ContentRulesRegistry>
-ChromeExtensionsAPIClient::CreateContentRulesRegistry(
-    content::BrowserContext* browser_context,
-    RulesCacheDelegate* cache_delegate) const {
-  return base::MakeRefCounted<ChromeContentRulesRegistry>(
-      browser_context, cache_delegate,
-      base::BindOnce(&CreateDefaultContentPredicateEvaluators,
-                     base::Unretained(browser_context)));
-}
-
 std::unique_ptr<DevicePermissionsPrompt>
 ChromeExtensionsAPIClient::CreateDevicePermissionsPrompt(
     content::WebContents* web_contents) const {
diff --git a/chrome/browser/extensions/app_tab_helper.cc b/chrome/browser/extensions/app_tab_helper.cc
new file mode 100644
index 0000000..be30600
--- /dev/null
+++ b/chrome/browser/extensions/app_tab_helper.cc
@@ -0,0 +1,203 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/app_tab_helper.h"
+
+#include <memory>
+
+#include "base/check_op.h"
+#include "base/functional/bind.h"
+#include "base/notreached.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sessions/session_tab_helper_factory.h"
+#include "chrome/browser/shell_integration.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/web_applications/web_app_helpers.h"
+#include "chrome/common/buildflags.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/common/url_constants.h"
+#include "components/sessions/content/session_tab_helper.h"
+#include "content/public/browser/invalidate_type.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/disable_reason.h"
+#include "extensions/browser/extension_util.h"
+#include "extensions/browser/extension_web_contents_observer.h"
+#include "extensions/browser/image_loader.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_resource.h"
+#include "extensions/common/extension_urls.h"
+#include "extensions/common/feature_switch.h"
+#include "extensions/common/icons/extension_icon_set.h"
+#include "extensions/common/manifest.h"
+#include "extensions/common/manifest_handlers/icons_handler.h"
+#include "ui/gfx/image/image.h"
+#include "url/url_constants.h"
+
+#if BUILDFLAG(ENABLE_SESSION_SERVICE)
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sessions/session_service_factory.h"
+#endif
+
+using content::WebContents;
+
+namespace extensions {
+
+AppTabHelper::~AppTabHelper() = default;
+
+AppTabHelper::AppTabHelper(content::WebContents* web_contents)
+    : content::WebContentsObserver(web_contents),
+      content::WebContentsUserData<AppTabHelper>(*web_contents),
+      profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())) {
+  // Ensure we have a SessionTabHelper.
+  CreateSessionServiceTabHelper(web_contents);
+
+  registry_observation_.Observe(ExtensionRegistry::Get(profile_));
+}
+
+void AppTabHelper::SetExtensionApp(const Extension* extension) {
+  DCHECK(!extension || AppLaunchInfo::GetFullLaunchURL(extension).is_valid());
+  if (extension_app_ == extension) {
+    return;
+  }
+
+  DCHECK(!extension || extension->is_app());
+
+  extension_app_ = extension;
+
+  UpdateExtensionAppIcon(extension_app_);
+
+#if BUILDFLAG(ENABLE_SESSION_SERVICE)
+  if (extension_app_) {
+    SessionService* session_service = SessionServiceFactory::GetForProfile(
+        Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
+    if (session_service) {
+      sessions::SessionTabHelper* session_tab_helper =
+          sessions::SessionTabHelper::FromWebContents(web_contents());
+      CHECK(session_tab_helper);
+      session_service->SetTabExtensionAppID(session_tab_helper->window_id(),
+                                            session_tab_helper->session_id(),
+                                            GetExtensionAppId());
+    }
+  }
+#endif
+}
+
+void AppTabHelper::SetExtensionAppById(const ExtensionId& extension_app_id) {
+  const Extension* extension = GetExtension(extension_app_id);
+  if (extension) {
+    SetExtensionApp(extension);
+  }
+}
+
+ExtensionId AppTabHelper::GetExtensionAppId() const {
+  return extension_app_ ? extension_app_->id() : ExtensionId();
+}
+
+SkBitmap* AppTabHelper::GetExtensionAppIcon() {
+  if (extension_app_icon_.empty()) {
+    return nullptr;
+  }
+
+  return &extension_app_icon_;
+}
+
+void AppTabHelper::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->HasCommitted() ||
+      !navigation_handle->IsInPrimaryMainFrame()) {
+    return;
+  }
+
+  content::BrowserContext* context = web_contents()->GetBrowserContext();
+  ExtensionRegistry* registry = ExtensionRegistry::Get(context);
+  const ExtensionSet& enabled_extensions = registry->enabled_extensions();
+
+  Browser* browser = chrome::FindBrowserWithTab(web_contents());
+  if (browser && (browser->is_type_app() || browser->is_type_app_popup())) {
+    const Extension* extension = registry->GetInstalledExtension(
+        web_app::GetAppIdFromApplicationName(browser->app_name()));
+    if (extension && AppLaunchInfo::GetFullLaunchURL(extension).is_valid()) {
+      DCHECK(extension->is_app());
+      SetExtensionApp(extension);
+    }
+  } else {
+    UpdateExtensionAppIcon(
+        enabled_extensions.GetExtensionOrAppByURL(navigation_handle->GetURL()));
+  }
+}
+
+void AppTabHelper::DidCloneToNewWebContents(WebContents* old_web_contents,
+                                            WebContents* new_web_contents) {
+  // When the WebContents that this is attached to is cloned, give the new clone
+  // a AppTabHelper and copy state over.
+  CreateForWebContents(new_web_contents);
+  AppTabHelper* new_helper = FromWebContents(new_web_contents);
+
+  new_helper->SetExtensionApp(extension_app_);
+  new_helper->extension_app_icon_ = extension_app_icon_;
+}
+
+const Extension* AppTabHelper::GetExtension(
+    const ExtensionId& extension_app_id) {
+  if (extension_app_id.empty()) {
+    return nullptr;
+  }
+
+  content::BrowserContext* context = web_contents()->GetBrowserContext();
+  return ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
+      extension_app_id);
+}
+
+void AppTabHelper::UpdateExtensionAppIcon(const Extension* extension) {
+  extension_app_icon_.reset();
+  // Ensure previously enqueued callbacks are ignored.
+  image_loader_ptr_factory_.InvalidateWeakPtrs();
+
+  // Enqueue OnImageLoaded callback.
+  if (extension) {
+    ImageLoader* loader = ImageLoader::Get(profile_);
+    loader->LoadImageAsync(
+        extension,
+        IconsInfo::GetIconResource(extension,
+                                   extension_misc::EXTENSION_ICON_SMALL,
+                                   ExtensionIconSet::Match::kBigger),
+        gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
+                  extension_misc::EXTENSION_ICON_SMALL),
+        base::BindOnce(&AppTabHelper::OnImageLoaded,
+                       image_loader_ptr_factory_.GetWeakPtr()));
+  }
+}
+
+void AppTabHelper::OnImageLoaded(const gfx::Image& image) {
+  if (!image.IsEmpty()) {
+    extension_app_icon_ = *image.ToSkBitmap();
+    web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
+  }
+}
+
+void AppTabHelper::OnExtensionUnloaded(content::BrowserContext* browser_context,
+                                       const Extension* extension,
+                                       UnloadedExtensionReason reason) {
+  if (!extension_app_) {
+    return;
+  }
+
+  if (extension == extension_app_) {
+    SetExtensionApp(nullptr);
+  }
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(AppTabHelper);
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/app_tab_helper.h b/chrome/browser/extensions/app_tab_helper.h
new file mode 100644
index 0000000..2e680cf
--- /dev/null
+++ b/chrome/browser/extensions/app_tab_helper.h
@@ -0,0 +1,108 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_APP_TAB_HELPER_H_
+#define CHROME_BROWSER_EXTENSIONS_APP_TAB_HELPER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "extensions/common/extension_id.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace gfx {
+class Image;
+}
+
+namespace extensions {
+
+// Handles apps for the extensions system.
+class AppTabHelper : public content::WebContentsObserver,
+                     public ExtensionRegistryObserver,
+                     public content::WebContentsUserData<AppTabHelper> {
+ public:
+  AppTabHelper(const AppTabHelper&) = delete;
+  AppTabHelper& operator=(const AppTabHelper&) = delete;
+
+  ~AppTabHelper() override;
+
+  // Sets the extension denoting this as an app. If `extension` is non-null this
+  // tab becomes an app-tab. WebContents does not listen for unload events for
+  // the extension. It's up to consumers of WebContents to do that.
+  //
+  // NOTE: this should only be manipulated before the tab is added to a browser.
+  // TODO(sky): resolve if this is the right way to identify an app tab. If it
+  // is, then this should be passed in the constructor.
+  void SetExtensionApp(const Extension* extension);
+
+  // Convenience for setting the app extension by id. This does nothing if
+  // `extension_app_id` is empty, or an extension can't be found given the
+  // specified id.
+  void SetExtensionAppById(const ExtensionId& extension_app_id);
+
+  // Returns true if an app extension has been set.
+  bool is_app() const { return extension_app_ != nullptr; }
+
+  // Return ExtensionId for extension app.
+  // If an app extension has not been set, returns empty id.
+  ExtensionId GetExtensionAppId() const;
+
+  // If an app extension has been explicitly set for this WebContents its icon
+  // is returned.
+  //
+  // NOTE: the returned icon is larger than 16x16 (its size is
+  // extension_misc::EXTENSION_ICON_SMALLISH).
+  SkBitmap* GetExtensionAppIcon();
+
+ private:
+  friend class content::WebContentsUserData<AppTabHelper>;
+
+  explicit AppTabHelper(content::WebContents* web_contents);
+
+  // content::WebContentsObserver overrides.
+  void DidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  void DidCloneToNewWebContents(
+      content::WebContents* old_web_contents,
+      content::WebContents* new_web_contents) override;
+
+  // ExtensionRegistryObserver:
+  void OnExtensionUnloaded(content::BrowserContext* browser_context,
+                           const Extension* extension,
+                           UnloadedExtensionReason reason) override;
+
+  // Resets app_icon_ and if `extension` is non-null uses ImageLoader to load
+  // the extension's image asynchronously.
+  void UpdateExtensionAppIcon(const Extension* extension);
+
+  const Extension* GetExtension(const ExtensionId& extension_app_id);
+
+  void OnImageLoaded(const gfx::Image& image);
+
+  raw_ptr<Profile> profile_ = nullptr;
+
+  // If non-null this tab is an app tab and this is the extension the tab was
+  // created for.
+  raw_ptr<const Extension> extension_app_ = nullptr;
+
+  // Icon for extension_app_ (if non-null) or a manually-set icon for
+  // non-extension apps.
+  SkBitmap extension_app_icon_;
+
+  base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver>
+      registry_observation_{this};
+
+  // Vend weak pointers that can be invalidated to stop in-progress loads.
+  base::WeakPtrFactory<AppTabHelper> image_loader_ptr_factory_{this};
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_APP_TAB_HELPER_H_
diff --git a/chrome/browser/extensions/app_tab_helper_unittest.cc b/chrome/browser/extensions/app_tab_helper_unittest.cc
new file mode 100644
index 0000000..62c061d
--- /dev/null
+++ b/chrome/browser/extensions/app_tab_helper_unittest.cc
@@ -0,0 +1,83 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/app_tab_helper.h"
+
+#include "base/run_loop.h"
+#include "chrome/browser/extensions/extension_service_test_with_install.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/test_browser_window.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/public/test/web_contents_tester.h"
+#include "extensions/browser/extension_registrar.h"
+#include "extensions/test/test_extension_dir.h"
+#include "url/origin.h"
+
+namespace extensions {
+
+class AppTabHelperUnitTest : public ExtensionServiceTestWithInstall {
+ public:
+  void SetUp() override {
+    ExtensionServiceTestWithInstall::SetUp();
+    InitializeEmptyExtensionService();
+
+    std::unique_ptr<content::WebContents> web_contents(
+        content::WebContentsTester::CreateTestWebContents(profile(), nullptr));
+    web_contents_tester_ = content::WebContentsTester::For(web_contents.get());
+    AppTabHelper::CreateForWebContents(web_contents.get());
+    app_tab_helper_ = AppTabHelper::FromWebContents(web_contents.get());
+    browser()->tab_strip_model()->AppendWebContents(std::move(web_contents),
+                                                    true);
+  }
+
+  void TearDown() override {
+    app_tab_helper_ = nullptr;
+    web_contents_tester_ = nullptr;
+    // Remove any tabs in the tab strip to avoid test crashes.
+    if (browser_) {
+      while (!browser_->tab_strip_model()->empty()) {
+        browser_->tab_strip_model()->DetachAndDeleteWebContentsAt(0);
+      }
+    }
+    ExtensionServiceTestBase::TearDown();
+  }
+
+  Browser* browser() {
+    if (!browser_) {
+      Browser::CreateParams params(profile(), true);
+      browser_window_ = std::make_unique<TestBrowserWindow>();
+      params.window = browser_window_.get();
+      browser_.reset(Browser::Create(params));
+    }
+    return browser_.get();
+  }
+
+  AppTabHelper* app_tab_helper() { return app_tab_helper_; }
+
+ private:
+  // The browser and accompanying window.
+  std::unique_ptr<Browser> browser_;
+  std::unique_ptr<TestBrowserWindow> browser_window_;
+
+  raw_ptr<content::WebContentsTester> web_contents_tester_ = nullptr;
+  raw_ptr<AppTabHelper> app_tab_helper_ = nullptr;
+};
+
+TEST_F(AppTabHelperUnitTest, ClearsExtensionOnUnload) {
+  const Extension* extension =
+      PackAndInstallCRX(data_dir().AppendASCII("hosted_app"), INSTALL_NEW);
+  ASSERT_TRUE(extension);
+
+  app_tab_helper()->SetExtensionApp(extension);
+  EXPECT_EQ(extension->id(), app_tab_helper()->GetExtensionAppId());
+  EXPECT_TRUE(app_tab_helper()->is_app());
+  registrar()->RemoveExtension(extension->id(),
+                               UnloadedExtensionReason::TERMINATE);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(ExtensionId(), app_tab_helper()->GetExtensionAppId());
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_action_runner.h b/chrome/browser/extensions/extension_action_runner.h
index f273ee0d..af5641f 100644
--- a/chrome/browser/extensions/extension_action_runner.h
+++ b/chrome/browser/extensions/extension_action_runner.h
@@ -39,6 +39,8 @@
 
 // The provider for ExtensionActions corresponding to scripts which are actively
 // running or need permission.
+// TODO(crbug.com/393179880): Port this to desktop Android, then update
+// SitePermissionsHelperUnitTest and SitePermissionsHelperBrowserTest.
 class ExtensionActionRunner : public content::WebContentsObserver,
                               public ExtensionRegistryObserver {
  public:
diff --git a/chrome/browser/extensions/extension_browser_window_helper.cc b/chrome/browser/extensions/extension_browser_window_helper.cc
index b4d3b74..65f3100 100644
--- a/chrome/browser/extensions/extension_browser_window_helper.cc
+++ b/chrome/browser/extensions/extension_browser_window_helper.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/extensions/extension_browser_window_helper.h"
 
-#include "chrome/browser/extensions/tab_helper.h"
+#include "chrome/browser/extensions/app_tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_command_controller.h"
@@ -18,6 +18,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
 #include "extensions/browser/unloaded_extension_reason.h"
+#include "extensions/common/constants.h"
 #include "url/origin.h"
 
 namespace extensions {
@@ -50,7 +51,7 @@
   // Case 2: Check if the page is a page associated with a hosted app, which
   // can have non-extension schemes. For example, the Gmail hosted app would
   // have a URL of https://mail.google.com.
-  if (TabHelper::FromWebContents(web_contents)->GetExtensionAppId() ==
+  if (AppTabHelper::FromWebContents(web_contents)->GetExtensionAppId() ==
       extension->id()) {
     return true;
   }
diff --git a/chrome/browser/extensions/permissions/active_tab_unittest.cc b/chrome/browser/extensions/permissions/active_tab_unittest.cc
index 9526e1f..c184269 100644
--- a/chrome/browser/extensions/permissions/active_tab_unittest.cc
+++ b/chrome/browser/extensions/permissions/active_tab_unittest.cc
@@ -34,6 +34,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/test_extension_registry_observer.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
@@ -43,6 +44,8 @@
 #include "extensions/common/permissions/permissions_data.h"
 #include "extensions/test/test_extension_dir.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 using extensions::mojom::APIPermissionID;
 
 namespace extensions {
@@ -70,7 +73,6 @@
   PERMITTED_BOTH
 };
 
-// TODO(crbug.com/393179880): Port to desktop Android when TabHelper is ported.
 class ActiveTabTest : public ChromeRenderViewHostTestHarness {
  protected:
   ActiveTabTest()
diff --git a/chrome/browser/extensions/permissions/site_permissions_helper.cc b/chrome/browser/extensions/permissions/site_permissions_helper.cc
index 907d488..e1b683a0 100644
--- a/chrome/browser/extensions/permissions/site_permissions_helper.cc
+++ b/chrome/browser/extensions/permissions/site_permissions_helper.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/extensions/permissions/site_permissions_helper.h"
 
-#include "chrome/browser/extensions/extension_action_runner.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/permissions/active_tab_permission_granter.h"
 #include "chrome/browser/extensions/permissions/scripting_permissions_modifier.h"
@@ -12,11 +11,17 @@
 #include "chrome/browser/profiles/profile.h"
 #include "components/sessions/content/session_tab_helper.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/browser/blocked_action_type.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/permissions_manager.h"
 #include "extensions/common/extension.h"
+#include "extensions/common/permissions/permissions_data.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/extension_action_runner.h"
+#endif
+
 namespace extensions {
 
 namespace {
@@ -30,6 +35,7 @@
 constexpr int kRefreshRequiredActionsMask =
     BLOCKED_ACTION_WEB_REQUEST | BLOCKED_ACTION_SCRIPT_AT_START;
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 std::vector<ExtensionId> GetExtensionIds(
     const std::vector<const Extension*>& extensions) {
   std::vector<ExtensionId> extension_ids;
@@ -39,6 +45,7 @@
   }
   return extension_ids;
 }
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 }  // namespace
 
@@ -106,11 +113,16 @@
     content::WebContents* web_contents,
     PermissionsManager::UserSiteAccess new_access) {
   auto current_url = web_contents->GetLastCommittedURL();
-  bool reload_required = false;
 
   auto* permissions_manager = PermissionsManager::Get(profile_);
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  // TODO(crbug.com/393179880): Port ExtensionActionRunner to desktop Android.
   ExtensionActionRunner* action_runner =
       ExtensionActionRunner::GetForWebContents(web_contents);
+  bool reload_required = false;
+#else
+  NOTIMPLEMENTED_LOG_ONCE();
+#endif
 
   for (auto const* extension : extensions) {
     auto current_access =
@@ -165,6 +177,7 @@
         break;
     }
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     // Clear extension's tab permission when revoking user site permissions.
     bool revoking_current_site_permissions =
         new_access == PermissionsManager::UserSiteAccess::kOnClick;
@@ -194,14 +207,17 @@
     } else if (blocked_actions != BLOCKED_ACTION_NONE) {
       action_runner->RunBlockedActions(extension);
     }
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   }
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (action_runner && reload_required) {
     // Show the reload bubble for all extensions, since it could be confusing to
     // the user why only some of them appear on the dialog.
     std::vector<ExtensionId> extension_ids = GetExtensionIds(extensions);
     action_runner->ShowReloadPageBubble(extension_ids);
   }
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 }
 
 bool SitePermissionsHelper::PageNeedsRefreshToRun(int blocked_actions) {
@@ -211,9 +227,15 @@
 bool SitePermissionsHelper::HasBeenBlocked(
     const Extension& extension,
     content::WebContents* web_contents) const {
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  // TODO(crbug.com/393179880): Port ExtensionActionRunner to desktop Android.
   ExtensionActionRunner* action_runner =
       ExtensionActionRunner::GetForWebContents(web_contents);
   return action_runner && action_runner->WantsToRun(&extension);
+#else
+  NOTIMPLEMENTED_LOG_ONCE();
+  return false;
+#endif
 }
 
 bool SitePermissionsHelper::ShowAccessRequestsInToolbar(
diff --git a/chrome/browser/extensions/permissions/site_permissions_helper.h b/chrome/browser/extensions/permissions/site_permissions_helper.h
index d9568261..6daea5a6 100644
--- a/chrome/browser/extensions/permissions/site_permissions_helper.h
+++ b/chrome/browser/extensions/permissions/site_permissions_helper.h
@@ -7,6 +7,9 @@
 
 #include "base/memory/raw_ptr.h"
 #include "extensions/browser/permissions_manager.h"
+#include "extensions/buildflags/buildflags.h"
+
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
 class Profile;
 
diff --git a/chrome/browser/extensions/permissions/site_permissions_helper_browsertest.cc b/chrome/browser/extensions/permissions/site_permissions_helper_browsertest.cc
index 5d44a94..99491ebd 100644
--- a/chrome/browser/extensions/permissions/site_permissions_helper_browsertest.cc
+++ b/chrome/browser/extensions/permissions/site_permissions_helper_browsertest.cc
@@ -35,6 +35,8 @@
 
 }  // namespace
 
+// TODO(crbug.com/393179880): Port this to desktop Android after
+// ExtensionActionRunner is ported.
 class SitePermissionsHelperBrowserTest : public ExtensionBrowserTest {
  public:
   SitePermissionsHelperBrowserTest() = default;
diff --git a/chrome/browser/extensions/permissions/site_permissions_helper_unittest.cc b/chrome/browser/extensions/permissions/site_permissions_helper_unittest.cc
index e486e24..449c407489 100644
--- a/chrome/browser/extensions/permissions/site_permissions_helper_unittest.cc
+++ b/chrome/browser/extensions/permissions/site_permissions_helper_unittest.cc
@@ -4,14 +4,15 @@
 
 #include "chrome/browser/extensions/permissions/site_permissions_helper.h"
 
+#include <memory>
+#include <vector>
+
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/extensions/extension_action_runner.h"
 #include "chrome/browser/extensions/extension_service_test_with_install.h"
 #include "chrome/browser/extensions/permissions/permissions_updater.h"
 #include "chrome/browser/extensions/permissions/scripting_permissions_modifier.h"
+#include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/test_browser_window.h"
 #include "components/crx_file/id_util.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/navigation_simulator.h"
@@ -24,6 +25,12 @@
 #include "extensions/common/permissions/permissions_data.h"
 #include "extensions/test/permissions_manager_waiter.h"
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/extension_action_runner.h"
+#endif
+
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 
 using UserSiteAccess = PermissionsManager::UserSiteAccess;
@@ -38,12 +45,10 @@
       const std::vector<std::string>& host_permissions,
       const std::vector<std::string>& permissions = {});
 
-  // Adds a new tab with `url` to the tab strip, and returns the WebContents
-  // associated with it.
+  // Adds a new tab with `url` to the simulated tab strip, and returns the
+  // WebContents associated with it.
   content::WebContents* AddTab(const GURL& url);
 
-  Browser* browser();
-
   SitePermissionsHelper* permissions_helper() {
     return permissions_helper_.get();
   }
@@ -54,9 +59,8 @@
   void TearDown() override;
 
  private:
-  // The browser and accompaying window.
-  std::unique_ptr<Browser> browser_;
-  std::unique_ptr<TestBrowserWindow> browser_window_;
+  // A simulated tab strip that just owns WebContents.
+  std::vector<std::unique_ptr<content::WebContents>> tabs_;
 
   // Site permissions helper being tested.
   std::unique_ptr<SitePermissionsHelper> permissions_helper_;
@@ -89,10 +93,9 @@
   std::unique_ptr<content::WebContents> web_contents(
       content::WebContentsTester::CreateTestWebContents(profile(), nullptr));
   content::WebContents* raw_contents = web_contents.get();
+  TabHelper::CreateForWebContents(raw_contents);
 
-  browser()->tab_strip_model()->AppendWebContents(std::move(web_contents),
-                                                  true);
-  EXPECT_EQ(browser()->tab_strip_model()->GetActiveWebContents(), raw_contents);
+  tabs_.push_back(std::move(web_contents));
 
   content::NavigationSimulator::NavigateAndCommitFromBrowser(raw_contents, url);
   EXPECT_EQ(url, raw_contents->GetLastCommittedURL());
@@ -100,16 +103,6 @@
   return raw_contents;
 }
 
-Browser* SitePermissionsHelperUnitTest::browser() {
-  if (!browser_) {
-    Browser::CreateParams params(profile(), true);
-    browser_window_ = std::make_unique<TestBrowserWindow>();
-    params.window = browser_window_.get();
-    browser_.reset(Browser::Create(params));
-  }
-  return browser_.get();
-}
-
 void SitePermissionsHelperUnitTest::SetUp() {
   ExtensionServiceTestBase::SetUp();
   InitializeEmptyExtensionService();
@@ -119,12 +112,10 @@
 }
 
 void SitePermissionsHelperUnitTest::TearDown() {
-  // Remove any tabs in the tab strip; else the test crashes.
-  if (browser_) {
-    while (!browser_->tab_strip_model()->empty()) {
-      browser_->tab_strip_model()->DetachAndDeleteWebContentsAt(0);
-    }
-  }
+  // Delete the WebContents in the simulated tab strip.
+  tabs_.clear();
+  permissions_manager_ = nullptr;
+  permissions_helper_.reset();
 
   ExtensionServiceTestBase::TearDown();
 }
@@ -282,6 +273,9 @@
             SiteInteraction::kNone);
 }
 
+// TODO(crbug.com/393179880): Port these tests to desktop Android after
+// ExtensionActionRunner is ported.
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // Tests that updating site access only applies to the specified extensions for
 // the current site.
 TEST_F(SitePermissionsHelperUnitTest, UpdateSiteAccess_OnlySiteSelected) {
@@ -461,5 +455,6 @@
       extension->permissions_data()->GetPageAccess(
           non_user_permitted_site, extension_misc::kUnknownTabId, nullptr));
 }
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/service_worker_tracking_browsertest.cc b/chrome/browser/extensions/service_worker_tracking_browsertest.cc
index 27bf3c6a..cb7273d7 100644
--- a/chrome/browser/extensions/service_worker_tracking_browsertest.cc
+++ b/chrome/browser/extensions/service_worker_tracking_browsertest.cc
@@ -117,7 +117,7 @@
 
   virtual std::string GetExtensionPageContent() const { return "<p>page</p>"; }
 
-  virtual void LoadServiceWorkerExtension() {
+  void LoadServiceWorkerExtension() {
     // Load a basic extension with a service worker and wait for the worker to
     // start running.
     static constexpr char kManifest[] =
@@ -815,9 +815,7 @@
     return R"(<script src="/page.js"></script>)";
   }
 
-  void LoadServiceWorkerExtension() override {
-    ServiceWorkerIdTrackingBrowserTest::LoadServiceWorkerExtension();
-
+  void LoadSubScopeServiceWorker() {
     // Code for a service worker that will be registered for a sub-scope
     // of the extension root scope. This service worker is not allowed
     // access to extension APIs, as it's not listed in the manifest.
@@ -825,13 +823,8 @@
       base::ScopedAllowBlockingForTesting allow_blocking;
       base::CreateDirectory(test_extension_dir()->UnpackedPath().Append(
           FILE_PATH_LITERAL("subscope")));
-      // NOTE: `setInterval` is used to keep the service worker alive
-      // for the duration of the test, preventing it from being stopped
-      // prematurely, which could lead to test flakiness.
-      // See crbug.com/417430921.
       test_extension_dir()->WriteFile(FILE_PATH_LITERAL("subscope/sw.js"), R"(
           console.log("subscope service worker");
-          setInterval(() => { console.log("keepalive"); }, 1000);
       )");
     }
 
@@ -841,12 +834,19 @@
         navigator.serviceWorker.register("subscope/sw.js").then(function() {
           // Wait until the service worker is active.
           return navigator.serviceWorker.ready;
-        }).then(function(r) {
-          console.log("registration successful");
         }).catch(function(err) {
           console.log("registration error: " + err.message);
         });
     )");
+
+    // Open the extension page, which will cause the sub-scope service
+    // worker to start. We wait for its registration here.
+    content::ServiceWorkerContext* sw_context =
+        GetServiceWorkerContext(profile());
+    service_worker_test_utils::TestServiceWorkerContextObserver
+        registration_observer(sw_context);
+    OpenExtensionTab();
+    registration_observer.WaitForRegistrationStored();
   }
 };
 
@@ -857,17 +857,12 @@
 // crbug.com/395536907.
 IN_PROC_BROWSER_TEST_F(ServiceWorkerSubScopeWorkerTrackingBrowserTest,
                        StoppingSubScopeWorkerDoesNotAffectExtensionWorker) {
-  ASSERT_NO_FATAL_FAILURE(LoadServiceWorkerExtensionAndOpenExtensionTab());
-
-  // Wait for a console message that confirms the service worker for
-  // the sub-scope has been registered. Note that we can't use
-  // ExtensionTestMessageListener here since extension APIs are not
-  // available.
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  content::WebContentsConsoleObserver console_observer(web_contents);
-  console_observer.SetPattern("registration successful");
-  ASSERT_TRUE(console_observer.Wait());
+  // Load the extension service worker. This method will wait for its
+  // registration to be stored and the service worker to be running.
+  ASSERT_NO_FATAL_FAILURE(LoadServiceWorkerExtension());
+  // Load the sub-scope service worker and open the extension tab.
+  // This method will wait for the registration to be stored.
+  ASSERT_NO_FATAL_FAILURE(LoadSubScopeServiceWorker());
 
   // Confirm that we are tracking the main extension service worker.
   std::optional<WorkerId> extension_service_worker_id =
diff --git a/chrome/browser/extensions/tab_helper.cc b/chrome/browser/extensions/tab_helper.cc
index ec4b921..5cd381ea 100644
--- a/chrome/browser/extensions/tab_helper.cc
+++ b/chrome/browser/extensions/tab_helper.cc
@@ -7,71 +7,46 @@
 #include <memory>
 
 #include "base/check_op.h"
-#include "base/functional/bind.h"
+#include "base/notimplemented.h"
 #include "base/notreached.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/activity_log/activity_log.h"
-#include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h"
-#include "chrome/browser/extensions/extension_action_runner.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/extensions/install_observer.h"
-#include "chrome/browser/extensions/install_tracker.h"
-#include "chrome/browser/extensions/install_tracker_factory.h"
 #include "chrome/browser/extensions/permissions/active_tab_permission_granter.h"
 #include "chrome/browser/extensions/permissions/site_permissions_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/session_tab_helper_factory.h"
-#include "chrome/browser/shell_integration.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_dialogs.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/common/buildflags.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
-#include "chrome/common/url_constants.h"
 #include "components/sessions/content/session_tab_helper.h"
 #include "content/public/browser/back_forward_cache.h"
-#include "content/public/browser/invalidate_type.h"
-#include "content/public/browser/navigation_controller.h"
-#include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
 #include "extensions/browser/api/declarative/rules_registry_service.h"
 #include "extensions/browser/api/declarative_content/content_rules_registry.h"
-#include "extensions/browser/api/declarative_net_request/web_contents_helper.h"
-#include "extensions/browser/disable_reason.h"
-#include "extensions/browser/extension_prefs.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/browser/extension_util.h"
 #include "extensions/browser/extension_web_contents_observer.h"
-#include "extensions/browser/image_loader.h"
 #include "extensions/browser/permissions_manager.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_resource.h"
-#include "extensions/common/extension_urls.h"
-#include "extensions/common/feature_switch.h"
-#include "extensions/common/icons/extension_icon_set.h"
 #include "extensions/common/manifest.h"
-#include "extensions/common/manifest_handlers/icons_handler.h"
 #include "extensions/common/permissions/api_permission.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
-#include "ui/gfx/image/image.h"
-#include "url/url_constants.h"
+
+#if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h"
+#endif
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/app_tab_helper.h"
+#include "chrome/browser/extensions/extension_action_runner.h"
+#endif
 
 #if BUILDFLAG(ENABLE_SESSION_SERVICE)
 #include "chrome/browser/sessions/session_service.h"
 #include "chrome/browser/sessions/session_service_factory.h"
 #endif
 
-using content::NavigationController;
-using content::NavigationEntry;
 using content::WebContents;
 
 namespace extensions {
@@ -100,10 +75,13 @@
     : content::WebContentsObserver(web_contents),
       content::WebContentsUserData<TabHelper>(*web_contents),
       profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
-      extension_app_(nullptr),
       script_executor_(std::make_unique<ScriptExecutor>(web_contents)),
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+      // TODO(crbug.com/393179880): Port ExtensionActionRunner to desktop
+      // Android.
       extension_action_runner_(
           std::make_unique<ExtensionActionRunner>(web_contents)),
+#endif
       declarative_net_request_helper_(web_contents) {
   // The ActiveTabPermissionManager requires a session ID; ensure this
   // WebContents has one.
@@ -116,6 +94,10 @@
   // tab id assignment can be done on desktop Android.
   ActiveTabPermissionGranter::CreateForWebContents(web_contents, tab_id,
                                                    profile_);
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  // TODO(crbug.com/393179880): Pull this creation out of TabHelper.
+  AppTabHelper::CreateForWebContents(web_contents);
+#endif
 
   ActivityLog::GetInstance(profile_)->ObserveScripts(script_executor_.get());
 
@@ -123,61 +105,20 @@
     registry->MonitorWebContentsForRuleEvaluation(this->web_contents());
   });
 
-  ExtensionWebContentsObserver::GetForWebContents(web_contents)->dispatcher()->
-      set_delegate(this);
+  ExtensionWebContentsObserver::GetForWebContents(web_contents)
+      ->dispatcher()
+      ->set_delegate(this);
 
   registry_observation_.Observe(
       ExtensionRegistry::Get(web_contents->GetBrowserContext()));
 
+#if !BUILDFLAG(IS_ANDROID)
+  // The Android bookmark manager is native UI, not web UI, so this event router
+  // isn't needed on desktop Android.
   BookmarkManagerPrivateDragEventRouter::CreateForWebContents(web_contents);
-}
-
-void TabHelper::SetExtensionApp(const Extension* extension) {
-  DCHECK(!extension || AppLaunchInfo::GetFullLaunchURL(extension).is_valid());
-  if (extension_app_ == extension) {
-    return;
-  }
-
-  DCHECK(!extension || extension->is_app());
-
-  extension_app_ = extension;
-
-  UpdateExtensionAppIcon(extension_app_);
-
-#if BUILDFLAG(ENABLE_SESSION_SERVICE)
-  if (extension_app_) {
-    SessionService* session_service = SessionServiceFactory::GetForProfile(
-        Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
-    if (session_service) {
-      sessions::SessionTabHelper* session_tab_helper =
-          sessions::SessionTabHelper::FromWebContents(web_contents());
-      session_service->SetTabExtensionAppID(session_tab_helper->window_id(),
-                                            session_tab_helper->session_id(),
-                                            GetExtensionAppId());
-    }
-  }
 #endif
 }
 
-void TabHelper::SetExtensionAppById(const ExtensionId& extension_app_id) {
-  const Extension* extension = GetExtension(extension_app_id);
-  if (extension) {
-    SetExtensionApp(extension);
-  }
-}
-
-ExtensionId TabHelper::GetExtensionAppId() const {
-  return extension_app_ ? extension_app_->id() : ExtensionId();
-}
-
-SkBitmap* TabHelper::GetExtensionAppIcon() {
-  if (extension_app_icon_.empty()) {
-    return nullptr;
-  }
-
-  return &extension_app_icon_;
-}
-
 void TabHelper::SetReloadRequired(
     PermissionsManager::UserSiteSetting site_setting) {
   switch (site_setting) {
@@ -244,8 +185,9 @@
           RulesRegistryService::Get(profile_->GetOriginalProfile());
       DCHECK_NE(rules_registry_service,
                 original_profile_rules_registry_service);
-      if (original_profile_rules_registry_service)
+      if (original_profile_rules_registry_service) {
         func(original_profile_rules_registry_service->content_rules_registry());
+      }
     }
   }
 }
@@ -263,8 +205,8 @@
 
   InvokeForContentRulesRegistries(
       [this, navigation_handle](ContentRulesRegistry* registry) {
-    registry->DidFinishNavigation(web_contents(), navigation_handle);
-  });
+        registry->DidFinishNavigation(web_contents(), navigation_handle);
+      });
 
   content::BrowserContext* context = web_contents()->GetBrowserContext();
   ExtensionRegistry* registry = ExtensionRegistry::Get(context);
@@ -273,19 +215,6 @@
   DisableBackForwardCacheIfNecessary(enabled_extensions, context,
                                      navigation_handle);
 
-  Browser* browser = chrome::FindBrowserWithTab(web_contents());
-  if (browser && (browser->is_type_app() || browser->is_type_app_popup())) {
-    const Extension* extension = registry->GetInstalledExtension(
-        web_app::GetAppIdFromApplicationName(browser->app_name()));
-    if (extension && AppLaunchInfo::GetFullLaunchURL(extension).is_valid()) {
-      DCHECK(extension->is_app());
-      SetExtensionApp(extension);
-    }
-  } else {
-    UpdateExtensionAppIcon(
-        enabled_extensions.GetExtensionOrAppByURL(navigation_handle->GetURL()));
-  }
-
   // Reset the `reload_required_` data member, since a page navigation acts as a
   // page refresh.
   reload_required_ = false;
@@ -294,12 +223,10 @@
 void TabHelper::DidCloneToNewWebContents(WebContents* old_web_contents,
                                          WebContents* new_web_contents) {
   // When the WebContents that this is attached to is cloned, give the new clone
-  // a TabHelper and copy state over.
+  // a TabHelper.
+  // TODO(jamescook): Do we still need to do this if we're not copying any
+  // state?
   CreateForWebContents(new_web_contents);
-  TabHelper* new_helper = FromWebContents(new_web_contents);
-
-  new_helper->SetExtensionApp(extension_app_);
-  new_helper->extension_app_icon_ = extension_app_icon_;
 }
 
 void TabHelper::WebContentsDestroyed() {
@@ -310,45 +237,14 @@
   reload_required_ = false;
 }
 
-const Extension* TabHelper::GetExtension(const ExtensionId& extension_app_id) {
-  if (extension_app_id.empty()) {
-    return nullptr;
-  }
-
-  content::BrowserContext* context = web_contents()->GetBrowserContext();
-  return ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
-      extension_app_id);
-}
-
-void TabHelper::UpdateExtensionAppIcon(const Extension* extension) {
-  extension_app_icon_.reset();
-  // Ensure previously enqueued callbacks are ignored.
-  image_loader_ptr_factory_.InvalidateWeakPtrs();
-
-  // Enqueue OnImageLoaded callback.
-  if (extension) {
-    ImageLoader* loader = ImageLoader::Get(profile_);
-    loader->LoadImageAsync(
-        extension,
-        IconsInfo::GetIconResource(extension,
-                                   extension_misc::EXTENSION_ICON_SMALL,
-                                   ExtensionIconSet::Match::kBigger),
-        gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
-                  extension_misc::EXTENSION_ICON_SMALL),
-        base::BindOnce(&TabHelper::OnImageLoaded,
-                       image_loader_ptr_factory_.GetWeakPtr()));
-  }
-}
-
-void TabHelper::OnImageLoaded(const gfx::Image& image) {
-  if (!image.IsEmpty()) {
-    extension_app_icon_ = *image.ToSkBitmap();
-    web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
-  }
-}
-
 WindowController* TabHelper::GetExtensionWindowController() const {
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  // TODO(crbug.com/393179880): Support this method.
   return ExtensionTabUtil::GetWindowControllerOfTab(web_contents());
+#else
+  NOTIMPLEMENTED_LOG_ONCE();
+  return nullptr;
+#endif
 }
 
 WebContents* TabHelper::GetAssociatedWebContents() const {
@@ -369,14 +265,6 @@
   // side effects of loading/unloading the extension.
   web_contents()->GetController().GetBackForwardCache().Flush();
 
-  if (!extension_app_) {
-    return;
-  }
-
-  if (extension == extension_app_) {
-    SetExtensionApp(nullptr);
-  }
-
   // Technically, the refresh is no longer needed if the unloaded extension was
   // the only one causing `refresh_required`. However, we would need to track
   // which are the extensions causing the reload, and sometimes it is not
diff --git a/chrome/browser/extensions/tab_helper.h b/chrome/browser/extensions/tab_helper.h
index 36f0fd79..58fd605 100644
--- a/chrome/browser/extensions/tab_helper.h
+++ b/chrome/browser/extensions/tab_helper.h
@@ -10,7 +10,6 @@
 #include <string>
 
 #include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "chrome/common/extensions/webstore_install_result.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -29,15 +28,11 @@
 class RenderFrameHost;
 }
 
-namespace gfx {
-class Image;
-}
-
 namespace extensions {
 class ExtensionActionRunner;
 class Extension;
 
-// Per-tab extension helper. Also handles non-extension apps.
+// Per-tab extension helper.
 class TabHelper : public content::WebContentsObserver,
                   public ExtensionFunctionDispatcher::Delegate,
                   public ExtensionRegistryObserver,
@@ -48,34 +43,6 @@
 
   ~TabHelper() override;
 
-  // Sets the extension denoting this as an app. If `extension` is non-null this
-  // tab becomes an app-tab. WebContents does not listen for unload events for
-  // the extension. It's up to consumers of WebContents to do that.
-  //
-  // NOTE: this should only be manipulated before the tab is added to a browser.
-  // TODO(sky): resolve if this is the right way to identify an app tab. If it
-  // is, then this should be passed in the constructor.
-  void SetExtensionApp(const Extension* extension);
-
-  // Convenience for setting the app extension by id. This does nothing if
-  // `extension_app_id` is empty, or an extension can't be found given the
-  // specified id.
-  void SetExtensionAppById(const ExtensionId& extension_app_id);
-
-  // Returns true if an app extension has been set.
-  bool is_app() const { return extension_app_ != nullptr; }
-
-  // Return ExtensionId for extension app.
-  // If an app extension has not been set, returns empty id.
-  ExtensionId GetExtensionAppId() const;
-
-  // If an app extension has been explicitly set for this WebContents its icon
-  // is returned.
-  //
-  // NOTE: the returned icon is larger than 16x16 (its size is
-  // extension_misc::EXTENSION_ICON_SMALLISH).
-  SkBitmap* GetExtensionAppIcon();
-
   // Sets whether the tab will require a page reload for applying
   // `site_setting`.
   void SetReloadRequired(PermissionsManager::UserSiteSetting site_setting);
@@ -88,9 +55,11 @@
     return script_executor_.get();
   }
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ExtensionActionRunner* extension_action_runner() {
     return extension_action_runner_.get();
   }
+#endif
 
   void OnWatchedPageChanged(const std::vector<std::string>& css_selectors);
 
@@ -124,50 +93,25 @@
                            const Extension* extension,
                            UnloadedExtensionReason reason) override;
 
-  // App extensions related methods:
-
-  // Resets app_icon_ and if `extension` is non-null uses ImageLoader to load
-  // the extension's image asynchronously.
-  void UpdateExtensionAppIcon(const Extension* extension);
-
-  const Extension* GetExtension(const ExtensionId& extension_app_id);
-
-  void OnImageLoaded(const gfx::Image& image);
-
   // Sends our tab ID to `render_frame_host`.
   void SetTabId(content::RenderFrameHost* render_frame_host);
 
   raw_ptr<Profile> profile_;
 
-  // If non-null this tab is an app tab and this is the extension the tab was
-  // created for.
-  raw_ptr<const Extension> extension_app_;
-
-  // Icon for extension_app_ (if non-null) or a manually-set icon for
-  // non-extension apps.
-  SkBitmap extension_app_icon_;
-
   std::unique_ptr<ScriptExecutor> script_executor_;
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   std::unique_ptr<ExtensionActionRunner> extension_action_runner_;
+#endif
 
   declarative_net_request::WebContentsHelper declarative_net_request_helper_;
 
   // Whether the tab needs a page reload to apply the user site settings.
   bool reload_required_ = false;
 
-  // Extensions that have dismissed site access requests for this tab's origin.
-  std::set<ExtensionId> dismissed_extensions_;
-
   base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver>
       registry_observation_{this};
 
-  // Vend weak pointers that can be invalidated to stop in-progress loads.
-  base::WeakPtrFactory<TabHelper> image_loader_ptr_factory_{this};
-
-  // Generic weak ptr factory for posting callbacks.
-  base::WeakPtrFactory<TabHelper> weak_ptr_factory_{this};
-
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 };
 
diff --git a/chrome/browser/extensions/tab_helper_unittest.cc b/chrome/browser/extensions/tab_helper_unittest.cc
index 9935e97..aab966b 100644
--- a/chrome/browser/extensions/tab_helper_unittest.cc
+++ b/chrome/browser/extensions/tab_helper_unittest.cc
@@ -7,16 +7,17 @@
 #include "base/run_loop.h"
 #include "chrome/browser/extensions/extension_service_test_with_install.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/test_browser_window.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/web_contents_tester.h"
 #include "extensions/browser/extension_registrar.h"
 #include "extensions/browser/permissions_manager.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/test/test_extension_dir.h"
 #include "url/origin.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 
 class TabHelperUnitTest : public ExtensionServiceTestWithInstall {
@@ -25,39 +26,21 @@
     ExtensionServiceTestWithInstall::SetUp();
     InitializeEmptyExtensionService();
 
-    std::unique_ptr<content::WebContents> web_contents(
-        content::WebContentsTester::CreateTestWebContents(profile(), nullptr));
-    web_contents_tester_ = content::WebContentsTester::For(web_contents.get());
-    TabHelper::CreateForWebContents(web_contents.get());
-    tab_helper_ = TabHelper::FromWebContents(web_contents.get());
-    browser()->tab_strip_model()->AppendWebContents(std::move(web_contents),
-                                                    true);
-
+    web_contents_ =
+        content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+    web_contents_tester_ = content::WebContentsTester::For(web_contents_.get());
+    TabHelper::CreateForWebContents(web_contents_.get());
+    tab_helper_ = TabHelper::FromWebContents(web_contents_.get());
     permissions_manager_ = PermissionsManager::Get(profile());
   }
 
   void TearDown() override {
     tab_helper_ = nullptr;
     web_contents_tester_ = nullptr;
-    // Remove any tabs in the tab strip to avoid test crashes.
-    if (browser_) {
-      while (!browser_->tab_strip_model()->empty()) {
-        browser_->tab_strip_model()->DetachAndDeleteWebContentsAt(0);
-      }
-    }
+    web_contents_.reset();
     ExtensionServiceTestBase::TearDown();
   }
 
-  Browser* browser() {
-    if (!browser_) {
-      Browser::CreateParams params(profile(), true);
-      browser_window_ = std::make_unique<TestBrowserWindow>();
-      params.window = browser_window_.get();
-      browser_.reset(Browser::Create(params));
-    }
-    return browser_.get();
-  }
-
   content::WebContentsTester* web_contents_tester() {
     return web_contents_tester_;
   }
@@ -67,29 +50,12 @@
   PermissionsManager* permissions_manager() { return permissions_manager_; }
 
  private:
-  // The browser and accompanying window.
-  std::unique_ptr<Browser> browser_;
-  std::unique_ptr<TestBrowserWindow> browser_window_;
-
-  raw_ptr<content::WebContentsTester> web_contents_tester_;
-  raw_ptr<TabHelper> tab_helper_;
-  raw_ptr<PermissionsManager> permissions_manager_;
+  std::unique_ptr<content::WebContents> web_contents_;
+  raw_ptr<content::WebContentsTester> web_contents_tester_ = nullptr;
+  raw_ptr<TabHelper> tab_helper_ = nullptr;
+  raw_ptr<PermissionsManager> permissions_manager_ = nullptr;
 };
 
-TEST_F(TabHelperUnitTest, ClearsExtensionOnUnload) {
-  const Extension* extension =
-      PackAndInstallCRX(data_dir().AppendASCII("hosted_app"), INSTALL_NEW);
-  ASSERT_TRUE(extension);
-
-  tab_helper()->SetExtensionApp(extension);
-  EXPECT_EQ(extension->id(), tab_helper()->GetExtensionAppId());
-  EXPECT_TRUE(tab_helper()->is_app());
-  registrar()->RemoveExtension(extension->id(),
-                               UnloadedExtensionReason::TERMINATE);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(ExtensionId(), tab_helper()->GetExtensionAppId());
-}
-
 TEST_F(TabHelperUnitTest, ReloadRequired_BlockAllExtensions) {
   static constexpr char kManifest[] =
       R"({
diff --git a/chrome/browser/extensions/updater/extension_updater.h b/chrome/browser/extensions/updater/extension_updater.h
index 696d74e..16f72b14 100644
--- a/chrome/browser/extensions/updater/extension_updater.h
+++ b/chrome/browser/extensions/updater/extension_updater.h
@@ -9,7 +9,6 @@
 #include <map>
 #include <memory>
 #include <optional>
-#include <queue>
 #include <set>
 #include <string>
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 8391a25..ebe3354 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -4835,6 +4835,11 @@
     "expiry_milestone": 140
   },
   {
+    "name": "fedcm-iframe-origin",
+    "owners": [ "cbiesinger@chromium.org", "web-identity-eng@google.com"],
+    "expiry_milestone": 140
+  },
+  {
     "name": "fedcm-lightweight-mode",
     "owners": ["ekovac@google.com", "web-identity-eng@google.com"],
     "expiry_milestone": 140
@@ -6491,6 +6496,11 @@
     "expiry_milestone": 140
   },
   {
+    "name": "most-visited-tiles-visual-deduplication",
+    "owners": [ "hitarthkothari@google.com", "ckitagawa@google.com", "clank-tab-dev@google.com" ],
+    "expiry_milestone": 145
+  },
+  {
     "name": "move-theme-prefs-to-specifics",
     "owners": [ "ankushkush@google.com", "chrome-sync-dev@google.com" ],
     "expiry_milestone": 136
@@ -10029,6 +10039,11 @@
     "expiry_milestone": 146
   },
   {
+    "name": "webnn-onnxruntime",
+    "owners": [ "reillyg@chromium.org", "rafael.cintron@microsoft.com" ],
+    "expiry_milestone": 146
+  },
+  {
     "name": "webpage-alternative-text-zoom",
     "owners": [ "rkgibson@google.com", "bling-flags@google.com" ],
     "expiry_milestone": 127
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 59305e5..855c93e 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2049,6 +2049,10 @@
 const char kFedCmIdPRegistrationDescription[] =
     "Enables RPs to get identity credentials from registered IdPs.";
 
+const char kFedCmIframeOriginName[] = "FedCmIframeOrigin";
+const char kFedCmIframeOriginDescription[] =
+    "Allows showing iframe origins in the FedCM UI, if requested by the IDP.";
+
 const char kFedCmMetricsEndpointName[] = "FedCmMetricsEndpoint";
 const char kFedCmMetricsEndpointDescription[] =
     "Allows the FedCM API to send performance measurement to the metrics "
@@ -2638,6 +2642,14 @@
     "When showing MV tiles, use a new scoring function to compute the score of "
     "each segment.";
 
+const char kMostVisitedTilesVisualDeduplicationName[] =
+    "Most Visited Tile: Visual deduplication filter";
+
+const char kMostVisitedTilesVisualDeduplicationDescription[] =
+    "When computing MV Tiles, remove tiles that are visual duplicates "
+    "(i.e., have the same title and the same hostname) of another tile with "
+    "higher score.";
+
 const char kCanvas2DLayersName[] =
     "Enables canvas 2D methods BeginLayer and EndLayer";
 const char kCanvas2DLayersDescription[] =
@@ -2654,18 +2666,27 @@
     "Enables additional, experimental features in Web Machine Learning Neural "
     "Network (WebNN) API. Requires the \"WebNN API\" flag to be enabled.";
 
+#if BUILDFLAG(IS_MAC)
 const char kWebNNCoreMLName[] = "Core ML backend for WebNN";
 const char kWebNNCoreMLDescription[] =
     "Enables using Core ML for GPU and "
     "NPU inference with the WebNN API. Disabling this flag enables a "
     "fallback to TFLite.";
+#endif  // BUILDFLAG(IS_MAC)
 
+#if BUILDFLAG(IS_WIN)
 const char kWebNNDirectMLName[] = "DirectML backend for WebNN";
 const char kWebNNDirectMLDescription[] =
     "Enables using DirectML for GPU and "
     "NPU inference with the WebNN API. Disabling this flag enables a "
     "fallback to TFLite.";
 
+const char kWebNNOnnxRuntimeName[] = "ONNX Runtime backend for WebNN";
+const char kWebNNOnnxRuntimeDescription[] =
+    "Enables using ONNX Runtime for CPU, GPU and NPU inference with the WebNN "
+    "API. Disabling this flag enables a fallback to DirectML or TFLite.";
+#endif  // BUILDFLAG(IS_WIN)
+
 const char kSystemProxyForSystemServicesName[] =
     "Enable system-proxy for selected system services";
 const char kSystemProxyForSystemServicesDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 8bc99bf..2bfe616 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1180,6 +1180,9 @@
 extern const char kFedCmIdPRegistrationName[];
 extern const char kFedCmIdPRegistrationDescription[];
 
+extern const char kFedCmIframeOriginName[];
+extern const char kFedCmIframeOriginDescription[];
+
 extern const char kFedCmLightweightModeName[];
 extern const char kFedCmLightweightModeDescription[];
 
@@ -1531,6 +1534,9 @@
 extern const char kMostVisitedTilesReselectName[];
 extern const char kMostVisitedTilesReselectDescription[];
 
+extern const char kMostVisitedTilesVisualDeduplicationName[];
+extern const char kMostVisitedTilesVisualDeduplicationDescription[];
+
 extern const char kCanvas2DLayersName[];
 extern const char kCanvas2DLayersDescription[];
 
@@ -1540,12 +1546,19 @@
 extern const char kExperimentalWebMachineLearningNeuralNetworkName[];
 extern const char kExperimentalWebMachineLearningNeuralNetworkDescription[];
 
+#if BUILDFLAG(IS_MAC)
 extern const char kWebNNCoreMLName[];
 extern const char kWebNNCoreMLDescription[];
+#endif  // BUILDFLAG(IS_MAC)
 
+#if BUILDFLAG(IS_WIN)
 extern const char kWebNNDirectMLName[];
 extern const char kWebNNDirectMLDescription[];
 
+extern const char kWebNNOnnxRuntimeName[];
+extern const char kWebNNOnnxRuntimeDescription[];
+#endif  // BUILDFLAG(IS_WIN)
+
 #if BUILDFLAG(IS_ANDROID)
 extern const char kNewEtc1EncoderName[];
 extern const char kNewEtc1EncoderDescription[];
diff --git a/chrome/browser/glic/e2e_test/glic_e2e_test.cc b/chrome/browser/glic/e2e_test/glic_e2e_test.cc
index ded413aa..e7e4d0c 100644
--- a/chrome/browser/glic/e2e_test/glic_e2e_test.cc
+++ b/chrome/browser/glic/e2e_test/glic_e2e_test.cc
@@ -14,6 +14,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/contextual_cueing/contextual_cueing_features.h"
 #include "chrome/browser/glic/fre/fre_util.h"
+#include "chrome/browser/glic/fre/glic_fre_dialog_view.h"
 #include "chrome/browser/glic/glic_keyed_service.h"
 #include "chrome/browser/glic/glic_keyed_service_factory.h"
 #include "chrome/browser/glic/glic_pref_names.h"
@@ -47,6 +48,7 @@
 
 namespace {
 
+using glic::test::internal::kGlicFreShowingDialogState;
 using glic::test::internal::kGlicWindowControllerState;
 
 constexpr base::FilePath::StringViewType kRecordingDirectoryPath =
@@ -151,7 +153,8 @@
     CHECK(test_account.has_value());
     sign_in_functions.TurnOnSync(*test_account, 0);
   } else {
-    ForceSigninAndModelExecutionCapability(browser()->profile());
+    SigninWithPrimaryAccount(browser()->profile());
+    SetModelExecutionCapability(browser()->profile(), true);
   }
 }
 
@@ -176,6 +179,25 @@
   LiveTest::TearDownOnMainThread();
 }
 
+ui::test::InteractiveTestApi::MultiStep GlicE2ETest::WaitForAndInstrumentFre() {
+  MultiStep steps(Steps(
+      UninstrumentWebContents(kGlicFreContentsElementId, false),
+      UninstrumentWebContents(kGlicFreHostElementId, false),
+      InAnyContext(ObserveState(kGlicFreShowingDialogState,
+                                window_controller().fre_controller()),
+                   WaitForState(kGlicFreShowingDialogState, true),
+                   Steps(InstrumentNonTabWebView(
+                             kGlicFreHostElementId,
+                             GlicFreDialogView::kWebViewElementIdForTesting),
+                         InstrumentInnerWebContents(kGlicFreContentsElementId,
+                                                    kGlicFreHostElementId, 0),
+                         WaitForWebContentsReady(kGlicFreContentsElementId)),
+                   StopObservingState(kGlicFreShowingDialogState))));
+
+  AddDescriptionPrefix(steps, "WaitForAndInstrumentFre");
+  return steps;
+}
+
 ui::test::InteractiveTestApi::MultiStep
 GlicE2ETest::WaitForAndInstrumentGlic() {
   MultiStep steps(Steps(
@@ -196,15 +218,6 @@
   return steps;
 }
 
-ui::test::InteractiveTestApi::MultiStep GlicE2ETest::ClickElement(
-    const WebContentsInteractionTestUtil::DeepQuery& where) {
-  MultiStep steps =
-      Steps(WaitForElementVisible(kGlicContentsElementId, {"body"}),
-            ExecuteJsAt(kGlicContentsElementId, where, "(el)=>el.click()"));
-  AddDescriptionPrefix(steps, "ClickElement");
-  return steps;
-}
-
 void GlicE2ETest::MaybeStartWebPageReplayForRecordingPath(
     const std::string recording_filename) {
   if (test_mode_ == kRealBackend) {
diff --git a/chrome/browser/glic/e2e_test/glic_e2e_test.h b/chrome/browser/glic/e2e_test/glic_e2e_test.h
index 50167f4..05602ab 100644
--- a/chrome/browser/glic/e2e_test/glic_e2e_test.h
+++ b/chrome/browser/glic/e2e_test/glic_e2e_test.h
@@ -41,10 +41,9 @@
 
   void PreRunTestOnMainThread() override;
 
-  MultiStep WaitForAndInstrumentGlic();
+  MultiStep WaitForAndInstrumentFre();
 
-  MultiStep ClickElement(
-      const WebContentsInteractionTestUtil::DeepQuery& where);
+  MultiStep WaitForAndInstrumentGlic();
 
   // Based on the test mode, do a UI signin flow (live mode or record mode),
   // or force signin a fake account (replay mode).
diff --git a/chrome/browser/glic/e2e_test/internal b/chrome/browser/glic/e2e_test/internal
index 51bbed3..851d0f7 160000
--- a/chrome/browser/glic/e2e_test/internal
+++ b/chrome/browser/glic/e2e_test/internal
@@ -1 +1 @@
-Subproject commit 51bbed30547c704692fb274c1a90099dc5001c7c
+Subproject commit 851d0f7cbbe4e97a8edc5b139756b305f509e43f
diff --git a/chrome/browser/glic/glic_keyed_service.cc b/chrome/browser/glic/glic_keyed_service.cc
index 985dad8..1e0b4c3 100644
--- a/chrome/browser/glic/glic_keyed_service.cc
+++ b/chrome/browser/glic/glic_keyed_service.cc
@@ -360,15 +360,7 @@
     const std::vector<uint8_t>& action_proto,
     const mojom::GetTabContextOptions& options,
     mojom::WebClientHandler::ActInFocusedTabCallback callback) {
-  if (!base::FeatureList::IsEnabled(features::kGlicActor)) {
-    mojom::ActInFocusedTabResultPtr result =
-        mojom::ActInFocusedTabResult::NewErrorReason(
-            mojom::ActInFocusedTabErrorReason::kUnknown);
-
-    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), std::move(result)));
-    return;
-  }
+  CHECK(base::FeatureList::IsEnabled(features::kGlicActor));
 
   optimization_guide::proto::BrowserAction action;
   if (!action.ParseFromArray(action_proto.data(), action_proto.size())) {
@@ -387,10 +379,8 @@
 }
 
 void GlicKeyedService::StopActorTask() {
-  if (!actor_controller_) {
-    return;
-  }
-
+  CHECK(base::FeatureList::IsEnabled(features::kGlicActor));
+  CHECK(actor_controller_);
   actor_controller_->StopTask();
 }
 
diff --git a/chrome/browser/glic/host/glic_page_handler.cc b/chrome/browser/glic/host/glic_page_handler.cc
index 1e0fc1bf9..483e7d6 100644
--- a/chrome/browser/glic/host/glic_page_handler.cc
+++ b/chrome/browser/glic/host/glic_page_handler.cc
@@ -449,10 +449,22 @@
   void ActInFocusedTab(const std::vector<uint8_t>& action_proto,
                        glic::mojom::GetTabContextOptionsPtr options,
                        ActInFocusedTabCallback callback) override {
+    if (!base::FeatureList::IsEnabled(features::kGlicActor)) {
+      receiver_.ReportBadMessage(
+          "ActInFocusedTab cannot be called without GlicActor enabled.");
+      return;
+    }
     glic_service_->ActInFocusedTab(action_proto, *options, std::move(callback));
   }
 
-  void StopActorTask() override { glic_service_->StopActorTask(); }
+  void StopActorTask() override {
+    if (!base::FeatureList::IsEnabled(features::kGlicActor)) {
+      receiver_.ReportBadMessage(
+          "StopActorTask cannot be called without GlicActor enabled.");
+      return;
+    }
+    glic_service_->StopActorTask();
+  }
 
   void CaptureScreenshot(CaptureScreenshotCallback callback) override {
     if (ShouldDoApiActivationGating()) {
diff --git a/chrome/browser/glic/test_support/interactive_test_util.cc b/chrome/browser/glic/test_support/interactive_test_util.cc
index a87f748..3fad30f 100644
--- a/chrome/browser/glic/test_support/interactive_test_util.cc
+++ b/chrome/browser/glic/test_support/interactive_test_util.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/glic/test_support/interactive_test_util.h"
 
 #include "base/scoped_observation_traits.h"
+#include "chrome/browser/glic/fre/glic_fre_controller.h"
 #include "chrome/browser/glic/widget/glic_window_controller.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/interaction/polling_state_observer.h"
@@ -13,6 +14,15 @@
 
 namespace internal {
 
+GlicFreShowingDialogObserver::GlicFreShowingDialogObserver(
+    GlicFreController* controller)
+    : PollingStateObserver(
+          [controller]() { return controller->IsShowingDialog(); }) {}
+GlicFreShowingDialogObserver::~GlicFreShowingDialogObserver() = default;
+
+DEFINE_STATE_IDENTIFIER_VALUE(GlicFreShowingDialogObserver,
+                              kGlicFreShowingDialogState);
+
 GlicWindowControllerStateObserver::GlicWindowControllerStateObserver(
     const GlicWindowController& controller)
     : PollingStateObserver([&controller]() { return controller.state(); }) {}
@@ -55,5 +65,7 @@
 
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kGlicHostElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kGlicContentsElementId);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kGlicFreHostElementId);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kGlicFreContentsElementId);
 
 }  // namespace glic::test
diff --git a/chrome/browser/glic/test_support/interactive_test_util.h b/chrome/browser/glic/test_support/interactive_test_util.h
index 9bc8add3..77752a09 100644
--- a/chrome/browser/glic/test_support/interactive_test_util.h
+++ b/chrome/browser/glic/test_support/interactive_test_util.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_GLIC_TEST_SUPPORT_INTERACTIVE_TEST_UTIL_H_
 
 #include "base/scoped_observation_traits.h"
+#include "chrome/browser/glic/fre/glic_fre_controller.h"
 #include "chrome/browser/glic/host/glic.mojom.h"
 #include "chrome/browser/glic/widget/glic_view.h"
 #include "chrome/browser/glic/widget/glic_window_controller.h"
@@ -31,6 +32,17 @@
 
 namespace internal {
 
+// Observes FRE showing dialog for changes to state().
+class GlicFreShowingDialogObserver
+    : public ui::test::PollingStateObserver<bool> {
+ public:
+  explicit GlicFreShowingDialogObserver(GlicFreController* controller);
+  ~GlicFreShowingDialogObserver() override;
+};
+
+DECLARE_STATE_IDENTIFIER_VALUE(GlicFreShowingDialogObserver,
+                               kGlicFreShowingDialogState);
+
 // Observes `controller` for changes to state().
 class GlicWindowControllerStateObserver
     : public ui::test::PollingStateObserver<GlicWindowController::State> {
@@ -102,6 +114,11 @@
 // The glic webview contents.
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kGlicContentsElementId);
 
+// The glic FRE WebUI web contents.
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kGlicFreHostElementId);
+// The glic FRE webview contents.
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kGlicFreContentsElementId);
+
 }  // namespace glic::test
 
 #endif  // CHROME_BROWSER_GLIC_TEST_SUPPORT_INTERACTIVE_TEST_UTIL_H_
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.h b/chrome/browser/metrics/chrome_metrics_service_client.h
index 9e00aa8..d50bbe5 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.h
+++ b/chrome/browser/metrics/chrome_metrics_service_client.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include <memory>
-#include <queue>
 #include <string>
 #include <string_view>
 
diff --git a/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc b/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
index 1f939df1..74e4586 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
+++ b/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <optional>
+#include <queue>
 
 #include "base/memory/raw_ptr.h"
 #include "base/strings/strcat.h"
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index 53c286b..4a5d0d68 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -631,6 +631,12 @@
       base::BindRepeating(
           &SystemNetworkContextManager::UpdateIPv6ReachabilityOverrideEnabled,
           base::Unretained(this)));
+
+  pref_change_registrar_.Add(
+      prefs::kTLS13EarlyDataEnabled,
+      base::BindRepeating(
+          &SystemNetworkContextManager::UpdateTLS13EarlyDataEnabled,
+          base::Unretained(this)));
 }
 
 SystemNetworkContextManager::~SystemNetworkContextManager() {
@@ -693,6 +699,9 @@
 #endif  // BUILDFLAG(IS_LINUX)
 
   registry->RegisterBooleanPref(prefs::kIPv6ReachabilityOverrideEnabled, false);
+
+  // Default value doesn't matter since this pref is only used when managed.
+  registry->RegisterBooleanPref(prefs::kTLS13EarlyDataEnabled, false);
 }
 
 // static
@@ -1058,6 +1067,16 @@
   content::GetNetworkService()->SetIPv6ReachabilityOverride(value);
 }
 
+void SystemNetworkContextManager::UpdateTLS13EarlyDataEnabled() {
+  bool is_managed =
+      local_state_->IsManagedPreference(prefs::kTLS13EarlyDataEnabled);
+  bool value =
+      is_managed
+          ? local_state_->GetBoolean(prefs::kTLS13EarlyDataEnabled)
+          : base::FeatureList::IsEnabled(net::features::kEnableTLS13EarlyData);
+  content::GetNetworkService()->SetTLS13EarlyDataEnabled(value);
+}
+
 // static
 StubResolverConfigReader*
     SystemNetworkContextManager::stub_resolver_config_reader_for_testing_ =
diff --git a/chrome/browser/net/system_network_context_manager.h b/chrome/browser/net/system_network_context_manager.h
index 794aae69..a5551f7d 100644
--- a/chrome/browser/net/system_network_context_manager.h
+++ b/chrome/browser/net/system_network_context_manager.h
@@ -235,6 +235,8 @@
 
   void UpdateIPv6ReachabilityOverrideEnabled();
 
+  void UpdateTLS13EarlyDataEnabled();
+
   // The PrefService to retrieve all the pref values.
   raw_ptr<PrefService> local_state_;
 
diff --git a/chrome/browser/offline_pages/android/offline_page_auto_fetcher_service.h b/chrome/browser/offline_pages/android/offline_page_auto_fetcher_service.h
index 7f3a4376..2cbe113 100644
--- a/chrome/browser/offline_pages/android/offline_page_auto_fetcher_service.h
+++ b/chrome/browser/offline_pages/android/offline_page_auto_fetcher_service.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_OFFLINE_PAGE_AUTO_FETCHER_SERVICE_H_
 
 #include <memory>
-#include <queue>
 #include <string>
 #include <utility>
 #include <vector>
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 82373bd..66e447e 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -999,6 +999,9 @@
   { key::kPostQuantumKeyAgreementEnabled,
     prefs::kPostQuantumKeyAgreementEnabled,
     base::Value::Type::BOOLEAN },
+  { key::kTLS13EarlyDataEnabled,
+    prefs::kTLS13EarlyDataEnabled,
+    base::Value::Type::BOOLEAN },
   { key::kSSLErrorOverrideAllowed,
     prefs::kSSLErrorOverrideAllowed,
     base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/policy/policy_network_browsertest.cc b/chrome/browser/policy/policy_network_browsertest.cc
index 2a3356d..234d503 100644
--- a/chrome/browser/policy/policy_network_browsertest.cc
+++ b/chrome/browser/policy/policy_network_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <string_view>
 
+#include "base/compiler_specific.h"
 #include "base/functional/bind.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
@@ -36,7 +37,6 @@
 #include "third_party/boringssl/src/include/openssl/ssl.h"
 #include "url/gurl.h"
 
-
 namespace policy {
 
 class SSLPolicyTest : public PolicyTest {
@@ -343,4 +343,126 @@
   EXPECT_EQ(base::ASCIIToUTF16(kECHFailureTitle), result.title);
 }
 
+// TLS13EarlyDataPolicyTest relies on the fact that EmbeddedTestServer
+// uses HTTP/1.1 without connection reuse (unless the protocol is explicitly
+// specified). If EmbeddedTestServer ever gains connection reuse by default,
+// we'll need to force it off.
+class TLS13EarlyDataPolicyTest : public SSLPolicyTest,
+                                 public ::testing::WithParamInterface<bool> {
+ public:
+  static constexpr std::string_view kHostname = "a.test";
+  static constexpr std::string_view kEarlyDataCheckPath = "/test-request";
+  static constexpr std::string_view kEarlyDataAcceptedTitle = "accepted";
+  static constexpr std::string_view kEarlyDataNotAcceptedTitle = "not accepted";
+
+  TLS13EarlyDataPolicyTest()
+      : test_server_{net::EmbeddedTestServer::TYPE_HTTPS} {
+    std::vector<base::test::FeatureRef> enabled_features;
+    std::vector<base::test::FeatureRef> disabled_features;
+    if (GetParam()) {
+      enabled_features.emplace_back(net::features::kHappyEyeballsV3);
+    } else {
+      disabled_features.emplace_back(net::features::kHappyEyeballsV3);
+    }
+    disabled_features.emplace_back(net::features::kEnableTLS13EarlyData);
+    feature_list_.InitWithFeatures(enabled_features, disabled_features);
+  }
+
+  void SetUpOnMainThread() override {
+    net::SSLServerConfig server_config;
+    server_config.early_data_enabled = true;
+    test_server_.RegisterRequestHandler(
+        base::BindRepeating(&TLS13EarlyDataPolicyTest::HandleRequest));
+    test_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES,
+                              server_config);
+    ASSERT_TRUE(test_server_.Start());
+
+    host_resolver()->AddRule(kHostname, "127.0.0.1");
+  }
+
+  GURL GetURL(std::string_view path) {
+    return test_server_.GetURL(kHostname, path);
+  }
+
+  void NavigateToTestPage() {
+    ASSERT_TRUE(NavigateToUrl(GetURL("/index.html"), this));
+  }
+
+  std::string FetchResourceForEarlyDataCheck(GURL url) {
+    content::EvalJsResult result =
+        content::EvalJs(chrome_test_utils::GetActiveWebContents(this),
+                        content::JsReplace(R"(
+      fetch($1).then(res => res.text())
+    )",
+                                           GetURL(kEarlyDataCheckPath)));
+    return result.ExtractString();
+  }
+
+ private:
+  static std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) {
+    auto response = std::make_unique<net::test_server::BasicHttpResponse>();
+    response->AddCustomHeader("Connection", "close");
+    if (request.GetURL().path() == kEarlyDataCheckPath) {
+      response->set_content_type("text/plain; charset=utf-8");
+      if (request.ssl_info->early_data_accepted) {
+        response->set_content(kEarlyDataAcceptedTitle);
+      } else {
+        response->set_content(kEarlyDataNotAcceptedTitle);
+      }
+    } else {
+      response->set_content_type("text/html; charset=utf-8");
+      response->set_content("<html></html>");
+    }
+    return response;
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+  net::EmbeddedTestServer test_server_;
+};
+
+INSTANTIATE_TEST_SUITE_P(, TLS13EarlyDataPolicyTest, ::testing::Bool());
+
+IN_PROC_BROWSER_TEST_P(TLS13EarlyDataPolicyTest,
+                       TLS13EarlyDataPolicyNoOverride) {
+  EXPECT_FALSE(GetBooleanPref(prefs::kTLS13EarlyDataEnabled));
+
+  NavigateToTestPage();
+
+  EXPECT_EQ(FetchResourceForEarlyDataCheck(GetURL(kEarlyDataCheckPath)),
+            kEarlyDataNotAcceptedTitle);
+}
+
+// TODO(crbug.com/418717917): Flaky on Windows.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_TLS13EarlyDataPolicyEnable DISABLED_TLS13EarlyDataPolicyEnable
+#else
+#define MAYBE_TLS13EarlyDataPolicyEnable TLS13EarlyDataPolicyEnable
+#endif
+IN_PROC_BROWSER_TEST_P(TLS13EarlyDataPolicyTest,
+                       MAYBE_TLS13EarlyDataPolicyEnable) {
+  PolicyMap policies;
+  SetPolicy(&policies, key::kTLS13EarlyDataEnabled, base::Value(true));
+  UpdateProviderPolicy(policies);
+  EXPECT_TRUE(GetBooleanPref(prefs::kTLS13EarlyDataEnabled));
+  content::FlushNetworkServiceInstanceForTesting();
+
+  NavigateToTestPage();
+
+  EXPECT_EQ(FetchResourceForEarlyDataCheck(GetURL(kEarlyDataCheckPath)),
+            kEarlyDataAcceptedTitle);
+}
+
+IN_PROC_BROWSER_TEST_P(TLS13EarlyDataPolicyTest, TLS13EarlyDataPolicyDisable) {
+  PolicyMap policies;
+  SetPolicy(&policies, key::kTLS13EarlyDataEnabled, base::Value(false));
+  UpdateProviderPolicy(policies);
+  EXPECT_FALSE(GetBooleanPref(prefs::kTLS13EarlyDataEnabled));
+
+  NavigateToTestPage();
+
+  EXPECT_EQ(FetchResourceForEarlyDataCheck(GetURL(kEarlyDataCheckPath)),
+            kEarlyDataNotAcceptedTitle);
+}
+
 }  // namespace policy
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 4aab23c..18d9db77 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -288,7 +288,6 @@
     public static final String EXPLORE_OFFLINE_CONTENT_AVAILABILITY_STATUS =
             "Chrome.NTPExploreOfflineCard.HasExploreOfflineContent";
 
-    public static final String FIRST_RUN_CACHED_TOS_ACCEPTED = "first_run_tos_accepted";
     public static final String FIRST_RUN_FLOW_COMPLETE = "first_run_flow";
     // BACKUP_FLOW_SIGNIN_ACCOUNT_NAME used to be employed for the FRE too, thus the "first_run_"
     // prefix. The string should NOT be changed without some sort of migration.
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
index 8e4d031..9af3aeb 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/DeprecatedChromePreferenceKeys.java
@@ -144,6 +144,7 @@
                 "darken_websites_enabled",
                 "enhanced_bookmark_signin_promo_show_count",
                 "first_run_signin_complete",
+                "first_run_tos_accepted",
                 "fre_promo_opt_out",
                 "grid_tab_switcher_enabled",
                 "home_page_button_force_enabled",
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java
index 542187c..f237188 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java
@@ -53,7 +53,6 @@
                 ChromePreferenceKeys.DOWNLOAD_PENDING_DOWNLOAD_NOTIFICATIONS,
                 ChromePreferenceKeys.DOWNLOAD_PENDING_OMA_DOWNLOADS,
                 ChromePreferenceKeys.DOWNLOAD_UMA_ENTRY,
-                ChromePreferenceKeys.FIRST_RUN_CACHED_TOS_ACCEPTED,
                 ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE,
                 ChromePreferenceKeys.FIRST_RUN_FLOW_SIGNIN_SETUP,
                 ChromePreferenceKeys.FIRST_RUN_LIGHTWEIGHT_FLOW_COMPLETE,
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
index 1ce20956..7e75042d 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
@@ -1101,9 +1101,12 @@
     recorder.reason_ = SearchPrefetchServingReason::kNotDefaultSearchWithTerms;
     return prefetches_.end();
   }
-  // `HasCanonicalPreloadingOmniboxSearchURL` should return false if
-  // search_terms is empty.
-  CHECK(!search_terms.empty());
+  // TODO(https://crbug.com/417978876): figure out the reason why search_terms
+  // can be empty when `HasCanonicalPreloadingOmniboxSearchURL` returns true.
+  if (search_terms.empty()) {
+    recorder.reason_ = SearchPrefetchServingReason::kNotDefaultSearchWithTerms;
+    return prefetches_.end();
+  }
   recorder.search_terms_ = search_terms;
   const auto& iter = prefetches_.find(canonical_search_url);
 
diff --git a/chrome/browser/privacy_sandbox/notice/BUILD.gn b/chrome/browser/privacy_sandbox/notice/BUILD.gn
index 37c81cd..6251386 100644
--- a/chrome/browser/privacy_sandbox/notice/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/notice/BUILD.gn
@@ -92,7 +92,9 @@
       "//base",
       "//chrome/browser/profiles:profile",
       "//chrome/browser/ui/browser_window:browser_window",
+      "//chrome/browser/ui/privacy_sandbox:privacy_sandbox",
       "//chrome/common",
+      "//components/privacy_sandbox:features",
       "//components/tabs:public",
       "//content/public/browser",
     ]
diff --git a/chrome/browser/privacy_sandbox/notice/desktop_view_manager.cc b/chrome/browser/privacy_sandbox/notice/desktop_view_manager.cc
index 1cb7e52..482d508 100644
--- a/chrome/browser/privacy_sandbox/notice/desktop_view_manager.cc
+++ b/chrome/browser/privacy_sandbox/notice/desktop_view_manager.cc
@@ -9,6 +9,8 @@
 #include "chrome/browser/privacy_sandbox/notice/desktop_entrypoint_handlers.h"
 #include "chrome/browser/privacy_sandbox/notice/notice_model.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt.h"
+#include "components/privacy_sandbox/privacy_sandbox_features.h"
 
 namespace privacy_sandbox {
 
@@ -112,7 +114,16 @@
 
 void DesktopViewManager::HandleChromeOwnedPageNavigation(
     BrowserWindowInterface* browser_interface) {
-  // TODO(crbug.com/408016824): Call MaybeShowView.
+  // TODO(crbug.com/408016824): Move this Feature flag check to the orchestrator
+  // once implemented.
+  if (base::FeatureList::IsEnabled(
+          privacy_sandbox::kPrivacySandboxNoticeFramework)) {
+    // Create a view through the bound `Show` function.
+    MaybeCreateView(browser_interface,
+                    base::BindOnce(static_cast<void (*)(BrowserWindowInterface*,
+                                                        PrivacySandboxNotice)>(
+                        &PrivacySandboxDialog::Show)));
+  }
 }
 
 }  // namespace privacy_sandbox
diff --git a/chrome/browser/private_network_access/chrome_private_network_access_browsertest.cc b/chrome/browser/private_network_access/chrome_private_network_access_browsertest.cc
deleted file mode 100644
index 86b95c80..0000000
--- a/chrome/browser/private_network_access/chrome_private_network_access_browsertest.cc
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-#include <string_view>
-
-#include "base/memory/ref_counted.h"
-#include "base/test/bind.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
-#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/device_notifications/device_pinned_notification_renderer.h"
-#include "chrome/browser/device_notifications/device_status_icon_renderer.h"
-#include "chrome/browser/notifications/notification_display_service_tester.h"
-#include "chrome/browser/private_network_access/chrome_private_network_device_chooser.h"
-#include "chrome/browser/private_network_access/chrome_private_network_device_delegate.h"
-#include "chrome/browser/private_network_access/private_network_device_browser_test_utils.h"
-#include "chrome/browser/private_network_access/private_network_device_chooser_controller.h"
-#include "chrome/browser/private_network_access/private_network_device_permission_context.h"
-#include "chrome/browser/private_network_access/private_network_device_permission_context_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_dialogs.h"
-#include "chrome/browser/ui/chooser_bubble_testapi.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
-#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/common/content_client.h"
-#include "content/public/common/content_features.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/url_loader_interceptor.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "services/network/public/cpp/features.h"
-#include "services/network/public/mojom/ip_address_space.mojom-forward.h"
-#include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/common/origin_trials/trial_token.h"
-#include "third_party/blink/public/common/origin_trials/trial_token_result.h"
-#include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
-#include "third_party/blink/public/mojom/private_network_device/private_network_device.mojom.h"
-
-namespace {
-
-using ::content::JsReplace;
-using ::testing::Return;
-
-using Type = NewAcceptedDeviceType;
-
-constexpr std::string_view kUserAcceptedPrivateNetworkDeviceHistogramName =
-    "Security.PrivateNetworkAccess.PermissionNewAcceptedDeviceType";
-
-constexpr char kPrivateHost[] = "a.test";
-constexpr char kPublicPath[] =
-    "/private_network_access/no-favicon-treat-as-public-address.html";
-
-// Path to a response that passes Private Network Access checks.
-constexpr char kPnaLocalDevicePath[] = "/private_network_access/get-device";
-
-class ChromePrivateNetworkAccessTestBase : public InProcessBrowserTest {
- public:
-  void SetUpOnMainThread() override {
-    secure_server_.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
-    insecure_server_.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
-    ASSERT_TRUE(secure_server_.Start());
-    ASSERT_TRUE(insecure_server_.Start());
-    test_content_browser_client_.SetAsBrowserClient();
-    host_resolver()->AddRule(kPrivateHost, "127.0.0.1");
-
-    GURL url = SecureURL("/simple_page.html");
-    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-  }
-
-  void TearDownOnMainThread() override {
-    test_content_browser_client_.UnsetAsBrowserClient();
-  }
-
-  GURL SecureURL(const std::string& path) {
-    return secure_server_.GetURL("127.0.0.1", path);
-  }
-
-  GURL InsecureURL(const std::string& path) {
-    return insecure_server_.GetURL(kPrivateHost, path);
-  }
-
-  net::EmbeddedTestServer secure_server_{net::EmbeddedTestServer::TYPE_HTTPS};
-  net::EmbeddedTestServer insecure_server_{net::EmbeddedTestServer::TYPE_HTTP};
-
- protected:
-  base::test::ScopedFeatureList feature_list_;
-
- private:
-  TestPNAContentBrowserClient test_content_browser_client_;
-};
-class ChromePrivateNetworkAccessDisablePermissionFeatureTest
-    : public ChromePrivateNetworkAccessTestBase {
- public:
-  ChromePrivateNetworkAccessDisablePermissionFeatureTest() {
-    feature_list_.InitWithFeatures(
-        {features::kBlockInsecurePrivateNetworkRequests,
-         features::kBlockInsecurePrivateNetworkRequestsFromPrivate,
-         features::kPrivateNetworkAccessSendPreflights},
-        {network::features::kPrivateNetworkAccessPermissionPrompt,
-         network::features::kLocalNetworkAccessChecks});
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(ChromePrivateNetworkAccessDisablePermissionFeatureTest,
-                       RequestDevices) {
-  GURL url = SecureURL(kPublicPath);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  EXPECT_EQ(false,
-            content::EvalJs(web_contents,
-                            JsReplace(
-                                R"(fetch($1, {targetAddressSpace: 'local'})
-                          .then(
-                            response => response.ok,
-                            error => {
-                              console.log('Error fetching ' +$1, error);
-                              return false;
-                            })
-                          .catch(e => e.toString()))",
-                                InsecureURL(kPnaLocalDevicePath))));
-}
-
-class ChromePrivateNetworkAccessTest
-    : public ChromePrivateNetworkAccessTestBase {
- public:
-  ChromePrivateNetworkAccessTest() {
-    feature_list_.InitWithFeatures(
-        {network::features::kPrivateNetworkAccessPermissionPrompt,
-         features::kBlockInsecurePrivateNetworkRequests,
-         features::kBlockInsecurePrivateNetworkRequestsFromPrivate,
-         features::kPrivateNetworkAccessSendPreflights},
-        {network::features::kLocalNetworkAccessChecks});
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(ChromePrivateNetworkAccessTest, RequestDevices) {
-  base::HistogramTester histogram_tester;
-  GURL url = SecureURL(kPublicPath);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  EXPECT_EQ(true,
-            content::EvalJs(web_contents,
-                            JsReplace(
-                                R"(fetch($1, {targetAddressSpace: 'local'})
-                          .then(
-                            response => response.ok,
-                            error => {
-                              console.log('Error fetching ' +$1, error);
-                              return false;
-                            })
-                          .catch(e => e.toString()))",
-                                InsecureURL(kPnaLocalDevicePath))));
-  histogram_tester.ExpectUniqueSample(
-      kUserAcceptedPrivateNetworkDeviceHistogramName, Type::kValidDevice, 1);
-
-  EXPECT_EQ(false, content::EvalJs(web_contents,
-                                   JsReplace(
-                                       R"(fetch($1)
-                          .then(
-                            response => response.ok,
-                            error => {
-                              console.log('Error fetching ' +$1, error);
-                              return false;
-                            })
-                          .catch(e => e.toString()))",
-                                       InsecureURL(kPnaLocalDevicePath))));
-
-  EXPECT_EQ(false, content::EvalJs(
-                       web_contents,
-                       JsReplace(R"(fetch($1, {targetAddressSpace: 'private'})
-                          .then(
-                            response => response.ok,
-                            error => {
-                              console.log('Error fetching ' +$1, error);
-                              return false;
-                            })
-                          .catch(e => e.toString()))",
-                                 InsecureURL(kPnaLocalDevicePath))));
-
-  // Crash the renderer process.
-  content::RenderProcessHost* process =
-      web_contents->GetPrimaryMainFrame()->GetProcess();
-  content::RenderProcessHostWatcher crash_observer(
-      process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
-  process->Shutdown(0);
-  crash_observer.Wait();
-
-  // Reload tab.
-  chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  EXPECT_TRUE(content::WaitForLoadStop(
-      browser()->tab_strip_model()->GetActiveWebContents()));
-
-  // Fetch the same private network device again.
-  EXPECT_EQ(true, content::EvalJs(
-                      web_contents,
-                      JsReplace(R"(fetch($1, {targetAddressSpace: 'local'})
-                        .then(
-                          response => response.ok,
-                          error => {
-                            console.log('Error fetching ' +$1, error);
-                            return false;
-                            })
-                        .catch(e => e.toString()))",
-                                InsecureURL(kPnaLocalDevicePath))));
-  histogram_tester.ExpectUniqueSample(
-      kUserAcceptedPrivateNetworkDeviceHistogramName, Type::kValidDevice, 1);
-}
-
-}  // namespace
diff --git a/chrome/browser/resources/ash/settings/main_page_container/main_page_container.html b/chrome/browser/resources/ash/settings/main_page_container/main_page_container.html
index 7f17e518..ff68704 100644
--- a/chrome/browser/resources/ash/settings/main_page_container/main_page_container.html
+++ b/chrome/browser/resources/ash/settings/main_page_container/main_page_container.html
@@ -151,7 +151,8 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kNetwork)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kNetwork]]">
+    <page-displayer section="[[SectionEnum_.kNetwork]]"
+        aria-label="$i18n{internetPageTitle}">
       <settings-internet-page prefs="{{prefs}}">
       </settings-internet-page>
     </page-displayer>
@@ -159,7 +160,8 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kBluetooth)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kBluetooth]]">
+    <page-displayer section="[[SectionEnum_.kBluetooth]]"
+        aria-label="$i18n{bluetoothPageTitle}">
       <os-settings-bluetooth-page prefs="{{prefs}}">
       </os-settings-bluetooth-page>
     </page-displayer>
@@ -167,7 +169,8 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kMultiDevice)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kMultiDevice]]">
+    <page-displayer section="[[SectionEnum_.kMultiDevice]]"
+        aria-label="$i18n{multidevicePageTitle}">
       <settings-multidevice-page prefs="{{prefs}}">
       </settings-multidevice-page>
     </page-displayer>
@@ -175,7 +178,8 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kPeople)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kPeople]]">
+    <page-displayer section="[[SectionEnum_.kPeople]]"
+        aria-label="$i18n{osPeoplePageTitle}">
       <os-settings-people-page prefs="{{prefs}}">
       </os-settings-people-page>
     </page-displayer>
@@ -183,14 +187,16 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kKerberos)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kKerberos]]">
+    <page-displayer section="[[SectionEnum_.kKerberos]]"
+        aria-label="$i18n{kerberosPageTitle}">
       <settings-kerberos-page></settings-kerberos-page>
     </page-displayer>
   </template>
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kDevice)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kDevice]]">
+    <page-displayer section="[[SectionEnum_.kDevice]]"
+        aria-label="$i18n{devicePageTitle}">
       <settings-device-page prefs="{{prefs}}"
           languages="[[languages_]]"
           language-helper="[[languageHelper_]]">
@@ -200,7 +206,8 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kPersonalization)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kPersonalization]]">
+    <page-displayer section="[[SectionEnum_.kPersonalization]]"
+        aria-label="$i18n{personalizationPageTitle}">
       <settings-personalization-page prefs="{{prefs}}">
       </settings-personalization-page>
     </page-displayer>
@@ -208,7 +215,8 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kPrivacyAndSecurity)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kPrivacyAndSecurity]]">
+    <page-displayer section="[[SectionEnum_.kPrivacyAndSecurity]]"
+        aria-label="$i18n{privacyPageTitle}">
       <os-settings-privacy-page prefs="{{prefs}}">
       </os-settings-privacy-page>
     </page-displayer>
@@ -216,7 +224,8 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kApps)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kApps]]">
+    <page-displayer section="[[SectionEnum_.kApps]]"
+        aria-label="$i18n{appsPageTitle}">
       <os-settings-apps-page
           prefs="{{prefs}}"
           android-apps-info="[[androidAppsInfo]]">
@@ -226,7 +235,8 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kAccessibility)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kAccessibility]]">
+    <page-displayer section="[[SectionEnum_.kAccessibility]]"
+        aria-label="$i18n{a11yPageTitle}">
       <os-settings-a11y-page prefs="{{prefs}}"
           languages="[[languages_]]"
           language-helper="[[languageHelper_]]">
@@ -236,7 +246,8 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kSystemPreferences)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kSystemPreferences]]">
+    <page-displayer section="[[SectionEnum_.kSystemPreferences]]"
+        aria-label="$i18n{systemPreferencesTitle}">
       <settings-system-preferences-page prefs="{{prefs}}"
           languages="[[languages_]]"
           language-helper="[[languageHelper_]]">
@@ -246,7 +257,8 @@
   <template is="dom-if"
       if="[[shouldStampPage_(pageAvailability, SectionEnum_.kAboutChromeOs)]]"
       restamp>
-    <page-displayer section="[[SectionEnum_.kAboutChromeOs]]">
+    <page-displayer section="[[SectionEnum_.kAboutChromeOs]]"
+        aria-label="$i18n{aboutOsPageTitle}">
       <os-about-page prefs="{{prefs}}"></os-about-page>
     </page-displayer>
   </template>
diff --git a/chrome/browser/resources/ash/settings/main_page_container/page_displayer.html b/chrome/browser/resources/ash/settings/main_page_container/page_displayer.html
index b71afa2..2005af4a 100644
--- a/chrome/browser/resources/ash/settings/main_page_container/page_displayer.html
+++ b/chrome/browser/resources/ash/settings/main_page_container/page_displayer.html
@@ -1,5 +1,6 @@
 <style>
-  :host {
+  :host,
+  #focusHost {
     /* Do not show outline when focused. */
     outline: none;
   }
@@ -12,5 +13,6 @@
 <!-- This element should be focusable (e.g. After this page's respective menu
   item is selected). Set tabindex="-1" so this element is focusable, but not
   reachable via sequential keyboard navigation. -->
-<div id="focusHost" tabindex="-1"></div>
-<slot></slot>
+<div id="focusHost" tabindex="-1" aria-label="[[ariaLabel]]">
+  <slot></slot>
+</div>
diff --git a/chrome/browser/resources/ash/settings/main_page_container/page_displayer.ts b/chrome/browser/resources/ash/settings/main_page_container/page_displayer.ts
index 2ca0e55..59c2ccc 100644
--- a/chrome/browser/resources/ash/settings/main_page_container/page_displayer.ts
+++ b/chrome/browser/resources/ash/settings/main_page_container/page_displayer.ts
@@ -30,9 +30,12 @@
         type: Number,
         reflectToAttribute: true,
       },
+
+      ariaLabel: String,
     };
   }
 
+  override ariaLabel: string;
   active: boolean;
   section: Section;
 
@@ -43,7 +46,9 @@
   }
 
   override focus(): void {
-    this.shadowRoot!.getElementById('focusHost')!.focus();
+    // Since the focus host contains the whole page content, it should not cause
+    // a scroll jump when focused.
+    this.shadowRoot!.getElementById('focusHost')!.focus({preventScroll: true});
   }
 }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/dictation/macros/dictation_macros_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/dictation/macros/dictation_macros_test.js
index 9336eb4..d260f2e6 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/dictation/macros/dictation_macros_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv2/dictation/macros/dictation_macros_test.js
@@ -58,7 +58,8 @@
       assertEquals(undefined, runMacroResult.error);
     });
 
-AX_TEST_F('DictationMV2MacrosTest', 'ListCommandsMacro', async function() {
+// Disabled due to flaky test: crbug.com/413223744
+AX_TEST_F('DictationMV2MacrosTest', 'DISABLED_ListCommandsMacro', async function() {
   this.toggleDictationOn();
   const macro = await this.getSimpleParseStrategy().parse('help');
   assertEquals('LIST_COMMANDS', macro.getNameAsString());
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen_pumpkin_worker.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen_pumpkin_worker.ts
index e1c60cc..68dbc298 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen_pumpkin_worker.ts
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/dictation/offscreen_pumpkin_worker.ts
@@ -6,7 +6,8 @@
 
 import * as PumpkinConstants from './parse/pumpkin/pumpkin_constants.js';
 
-const SANDBOXED_PUMPKIN_TAGGER_JS_FILE = 'parse/sandboxed_pumpkin_tagger.js';
+const SANDBOXED_PUMPKIN_TAGGER_JS_FILE =
+    'dictation/parse/sandboxed_pumpkin_tagger.js';
 
 /**
  * Offscreen way to communicate to pumpkin via worker.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/BUILD.gn
index b0bb887..b9b1c16 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/BUILD.gn
@@ -259,7 +259,6 @@
   ]
 
   sources = [
-    "background/background.html",
     "earcons/chromevox_loaded.ogg",
     "earcons/chromevox_loading.ogg",
     "earcons/control.wav",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/background.html b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/background.html
deleted file mode 100644
index 4581db4..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/background.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- ChromeVox -->
-
-<!-- Closure files, with a blank line between levels of the dependency tree. -->
-
-<script src="../closure/base.js" charset="utf-8"></script>;
-
-<script src="../closure/debug/error.js" charset="utf-8"></script>
-<script src="../closure/dom/nodetype.js" charset="utf-8"></script>
-<script src="../closure/i18n/ordinalrules.js" charset="utf-8"></script>
-<script src="../closure/i18n/pluralrules.js" charset="utf-8"></script>
-<script src="../closure/string/string.js" charset="utf-8"></script>
-
-<script src="../closure/asserts/asserts.js" charset="utf-8"></script>
-
-<script src="../closure/i18n/messageformat.js" charset="utf-8"></script>
-
-<script src="../common/closure_loader.js" charset="utf-8"></script>
-
-<!--
-  ChromeVox ES6 module(s).
-
-  During the transition to ES6 modules, this top level module will import all
-  migrated modules. The migration will occur in breadth-first order, starting
-  with background.js. ES6 modules can "depend" on Closure files (since these are
-  global), but not vice versa.
--->
-<script type="module" src="es6_loader.js"></script>
-
-<!-- Third party -->
-<!-- Speech Rule Engine -->
-<script src="../../third_party/sre/sre_browser.js"></script>
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index f830341..5567976 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -50,6 +50,7 @@
     "//chrome/browser/new_tab_page/modules/v2/authentication:mojo_bindings_ts__generator",
     "//chrome/browser/new_tab_page/modules/v2/calendar:mojo_bindings_ts__generator",
     "//chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption:mojo_bindings_ts__generator",
+    "//chrome/browser/ui/webui/customize_buttons:mojo_bindings_ts__generator",
     "//chrome/browser/ui/webui/new_tab_page:mojo_bindings_ts__generator",
     "//chrome/browser/ui/webui/ntp_microsoft_auth:shared_ts__generator",
   ]
@@ -65,6 +66,7 @@
     "$root_gen_dir/chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption/url_visit_types.mojom-webui.ts",
     "$root_gen_dir/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom-webui.ts",
     "$root_gen_dir/chrome/browser/ui/webui/ntp_microsoft_auth/ntp_microsoft_auth_shared_ui.mojom-webui.ts",
+    "$root_gen_dir/chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom-webui.ts",
   ]
 
   if (!is_official_build) {
diff --git a/chrome/browser/resources/new_tab_page/app.ts b/chrome/browser/resources/new_tab_page/app.ts
index 35542e8..3179478 100644
--- a/chrome/browser/resources/new_tab_page/app.ts
+++ b/chrome/browser/resources/new_tab_page/app.ts
@@ -28,13 +28,16 @@
 import {getCss} from './app.css.js';
 import {getHtml} from './app.html.js';
 import {BackgroundManager} from './background_manager.js';
+import type {CustomizeButtonsDocumentCallbackRouter, CustomizeButtonsHandlerRemote} from './customize_buttons.mojom-webui.js';
+import {CustomizeChromeSection, SidePanelOpenTrigger} from './customize_buttons.mojom-webui.js';
+import {CustomizeButtonsProxy} from './customize_buttons_proxy.js';
 import {CustomizeDialogPage} from './customize_dialog_types.js';
 import type {IframeElement} from './iframe.js';
 import type {LogoElement} from './logo.js';
 import {recordDuration, recordLoadDuration} from './metrics_utils.js';
 import {ParentTrustedDocumentProxy} from './modules/microsoft_auth_frame_connector.js';
 import type {PageCallbackRouter, PageHandlerRemote, Theme} from './new_tab_page.mojom-webui.js';
-import {CustomizeChromeSection, IphFeature, NtpBackgroundImageSource} from './new_tab_page.mojom-webui.js';
+import {IphFeature, NtpBackgroundImageSource} from './new_tab_page.mojom-webui.js';
 import {NewTabPageProxy} from './new_tab_page_proxy.js';
 import type {MicrosoftAuthUntrustedDocumentRemote} from './ntp_microsoft_auth_shared_ui.mojom-webui.js';
 import {$$} from './utils.js';
@@ -289,6 +292,9 @@
 
   private callbackRouter_: PageCallbackRouter;
   private pageHandler_: PageHandlerRemote;
+  private customizeButtonsCallbackRouter_:
+      CustomizeButtonsDocumentCallbackRouter;
+  private customizeButtonsHandler_: CustomizeButtonsHandlerRemote;
   private backgroundManager_: BackgroundManager;
   private connectMicrosoftAuthToParentDocumentListenerId_: number|null = null;
   private setThemeListenerId_: number|null = null;
@@ -305,6 +311,9 @@
     super();
     this.callbackRouter_ = NewTabPageProxy.getInstance().callbackRouter;
     this.pageHandler_ = NewTabPageProxy.getInstance().handler;
+    this.customizeButtonsCallbackRouter_ =
+        CustomizeButtonsProxy.getInstance().callbackRouter;
+    this.customizeButtonsHandler_ = CustomizeButtonsProxy.getInstance().handler;
     this.backgroundManager_ = BackgroundManager.getInstance();
     this.shouldPrintPerformance_ =
         new URLSearchParams(location.search).has('print_perf');
@@ -375,13 +384,14 @@
           this.theme_ = theme;
         });
     this.setCustomizeChromeSidePanelVisibilityListener_ =
-        this.callbackRouter_.setCustomizeChromeSidePanelVisibility.addListener(
-            (visible: boolean) => {
-              this.showCustomize_ = visible;
-              if (!visible) {
-                this.showWallpaperSearch_ = false;
-              }
-            });
+        this.customizeButtonsCallbackRouter_
+            .setCustomizeChromeSidePanelVisibility.addListener(
+                (visible: boolean) => {
+                  this.showCustomize_ = visible;
+                  if (!visible) {
+                    this.showWallpaperSearch_ = false;
+                  }
+                });
     this.showWebstoreToastListenerId_ =
         this.callbackRouter_.showWebstoreToast.addListener(() => {
           if (this.showCustomize_) {
@@ -456,11 +466,11 @@
     this.callbackRouter_.removeListener(
         this.connectMicrosoftAuthToParentDocumentListenerId_!);
     this.callbackRouter_.removeListener(this.setThemeListenerId_!);
-    this.callbackRouter_.removeListener(
-        this.setCustomizeChromeSidePanelVisibilityListener_!);
     this.callbackRouter_.removeListener(this.showWebstoreToastListenerId_!);
     this.callbackRouter_.removeListener(
         this.setWallpaperSearchButtonVisibilityListener_!);
+    this.customizeButtonsCallbackRouter_.removeListener(
+        this.setCustomizeChromeSidePanelVisibilityListener_!);
     this.eventTracker_.removeAll();
   }
 
@@ -601,7 +611,7 @@
         ['ntp-customize-buttons', '#customizeButton'], {fixed: true});
     this.pageHandler_.maybeShowFeaturePromo(IphFeature.kCustomizeChrome);
     if (this.showWallpaperSearchButton_) {
-      this.pageHandler_.incrementWallpaperSearchButtonShownCount();
+      this.customizeButtonsHandler_.incrementWallpaperSearchButtonShownCount();
     }
   }
 
@@ -623,7 +633,7 @@
     this.selectedCustomizeDialogPage_ = null;
     this.setCustomizeChromeSidePanelVisible_(!this.showCustomize_);
     if (!this.showCustomize_) {
-      this.pageHandler_.incrementCustomizeChromeButtonOpenCount();
+      this.customizeButtonsHandler_.incrementCustomizeChromeButtonOpenCount();
       recordCustomizeChromeOpen(NtpCustomizeChromeEntryPoint.CUSTOMIZE_BUTTON);
     }
   }
@@ -658,7 +668,7 @@
     this.showWallpaperSearch_ = true;
     this.setCustomizeChromeSidePanelVisible_(this.showWallpaperSearch_);
     if (!this.showCustomize_) {
-      this.pageHandler_.incrementCustomizeChromeButtonOpenCount();
+      this.customizeButtonsHandler_.incrementCustomizeChromeButtonOpenCount();
       recordCustomizeChromeOpen(
           NtpCustomizeChromeEntryPoint.WALLPAPER_SEARCH_BUTTON);
     }
@@ -913,7 +923,8 @@
         section = CustomizeChromeSection.kWallpaperSearch;
         break;
     }
-    this.pageHandler_.setCustomizeChromeSidePanelVisible(visible, section);
+    this.customizeButtonsHandler_.setCustomizeChromeSidePanelVisible(
+        visible, section, SidePanelOpenTrigger.kNewTabPage);
   }
 
   private printPerformanceDatum_(
diff --git a/chrome/browser/resources/new_tab_page/customize_buttons_proxy.ts b/chrome/browser/resources/new_tab_page/customize_buttons_proxy.ts
new file mode 100644
index 0000000..8c386ac
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/customize_buttons_proxy.ts
@@ -0,0 +1,38 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {CustomizeButtonsDocumentCallbackRouter, CustomizeButtonsHandlerFactory, CustomizeButtonsHandlerRemote} from './customize_buttons.mojom-webui.js';
+
+let instance: CustomizeButtonsProxy|null = null;
+
+/** Holds Mojo interfaces for communication with the browser process. */
+export class CustomizeButtonsProxy {
+  static getInstance(): CustomizeButtonsProxy {
+    if (!instance) {
+      const handler = new CustomizeButtonsHandlerRemote();
+      const callbackRouter = new CustomizeButtonsDocumentCallbackRouter();
+      CustomizeButtonsHandlerFactory.getRemote().createCustomizeButtonsHandler(
+          callbackRouter.$.bindNewPipeAndPassRemote(),
+          handler.$.bindNewPipeAndPassReceiver());
+      instance = new CustomizeButtonsProxy(handler, callbackRouter);
+    }
+    return instance;
+  }
+
+  static setInstance(
+      handler: CustomizeButtonsHandlerRemote,
+      callbackRouter: CustomizeButtonsDocumentCallbackRouter) {
+    instance = new CustomizeButtonsProxy(handler, callbackRouter);
+  }
+
+  handler: CustomizeButtonsHandlerRemote;
+  callbackRouter: CustomizeButtonsDocumentCallbackRouter;
+
+  private constructor(
+      handler: CustomizeButtonsHandlerRemote,
+      callbackRouter: CustomizeButtonsDocumentCallbackRouter) {
+    this.handler = handler;
+    this.callbackRouter = callbackRouter;
+  }
+}
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.gni b/chrome/browser/resources/new_tab_page/new_tab_page.gni
index 6877384..d965236 100644
--- a/chrome/browser/resources/new_tab_page/new_tab_page.gni
+++ b/chrome/browser/resources/new_tab_page/new_tab_page.gni
@@ -8,6 +8,7 @@
 # List of files that don't need to be passed to html_to_wrapper().
 all_ts_files = [
                  "background_manager.ts",
+                 "customize_buttons_proxy.ts",
                  "customize_dialog_types.ts",
                  "i18n_setup.ts",
                  "image_processor.ts",
@@ -49,6 +50,7 @@
   # Transpiled Mojo JS files to be excluded from the bundle in optimized builds.
   mojo_js_files = [
     "calendar_data.mojom-webui.js",
+    "customize_buttons.mojom-webui.js",
     "drive_suggestion.mojom-webui.js",
     "file_suggestion.mojom-webui.js",
     "google_calendar.mojom-webui.js",
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.ts b/chrome/browser/resources/new_tab_page/new_tab_page.ts
index f4a81ea..c40572f1 100644
--- a/chrome/browser/resources/new_tab_page/new_tab_page.ts
+++ b/chrome/browser/resources/new_tab_page/new_tab_page.ts
@@ -21,6 +21,7 @@
 export {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 export {AppElement, CUSTOMIZE_CHROME_BUTTON_ELEMENT_ID, NtpCustomizeChromeEntryPoint, NtpElement} from './app.js';
 export {BackgroundManager} from './background_manager.js';
+export {CustomizeButtonsProxy} from './customize_buttons_proxy.js';
 export {CustomizeDialogPage} from './customize_dialog_types.js';
 export {DoodleShareDialogElement} from './doodle_share_dialog.js';
 export {IframeElement} from './iframe.js';
diff --git a/chrome/browser/resources/print_preview/data/model.ts b/chrome/browser/resources/print_preview/data/model.ts
index f6b047d..20dd772 100644
--- a/chrome/browser/resources/print_preview/data/model.ts
+++ b/chrome/browser/resources/print_preview/data/model.ts
@@ -807,6 +807,7 @@
         this.margins.get(CustomMarginsOrientation.BOTTOM) > 0;
   }
 
+  // <if expr="is_win or is_macosx">
   private updateRasterizeAvailable_() {
     // Need document settings to know if source is PDF.
     if (this.documentSettings === undefined) {
@@ -815,6 +816,7 @@
 
     this.setSettingPath_('rasterize.available', this.isRasterizeAvailable_());
   }
+  // </if>
 
   /**
    * @return Whether the rasterization setting should be available.
diff --git a/chrome/browser/resources/print_preview/ui/app.ts b/chrome/browser/resources/print_preview/ui/app.ts
index d4e1e15..1e3de7c 100644
--- a/chrome/browser/resources/print_preview/ui/app.ts
+++ b/chrome/browser/resources/print_preview/ui/app.ts
@@ -82,7 +82,7 @@
     };
   }
 
-  accessor state: State;
+  accessor state: State = State.NOT_READY;
   protected accessor controlsManaged_: boolean = false;
   protected accessor destination_: Destination;
   private accessor destinationsManaged_: boolean = false;
@@ -399,13 +399,6 @@
     }
   }
 
-  private onErrorChange_() {
-    if (this.error_ !== Error.NONE) {
-      this.nativeLayer_!.recordInHistogram(
-          'PrintPreview.StateError', this.error_, Error.MAX_BUCKET);
-    }
-  }
-
   protected onPrintRequested_() {
     if (this.state === State.NOT_READY) {
       this.printRequested_ = true;
diff --git a/chrome/browser/resources/print_preview/ui/button_strip.ts b/chrome/browser/resources/print_preview/ui/button_strip.ts
index b683ba83..5669f05 100644
--- a/chrome/browser/resources/print_preview/ui/button_strip.ts
+++ b/chrome/browser/resources/print_preview/ui/button_strip.ts
@@ -44,7 +44,7 @@
 
   accessor destination: Destination;
   accessor firstLoad: boolean = false;
-  accessor state: State;
+  accessor state: State = State.NOT_READY;
   protected accessor printButtonEnabled_: boolean = false;
   protected accessor printButtonLabel_: string =
       loadTimeData.getString('printButton');
diff --git a/chrome/browser/resources/print_preview/ui/copies_settings.ts b/chrome/browser/resources/print_preview/ui/copies_settings.ts
index 3a246ef..d18e008f 100644
--- a/chrome/browser/resources/print_preview/ui/copies_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/copies_settings.ts
@@ -55,10 +55,10 @@
     };
   }
 
-  accessor capability: CopiesCapability|null;
+  accessor capability: CopiesCapability|null = null;
   accessor disabled: boolean = false;
-  protected accessor copiesMax_: number;
-  protected accessor currentValue_: string;
+  protected accessor copiesMax_: number = DEFAULT_MAX_COPIES;
+  protected accessor currentValue_: string = '';
   protected accessor inputValid_: boolean = false;
   private accessor collateAvailable_: boolean = false;
 
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.ts b/chrome/browser/resources/print_preview/ui/destination_settings.ts
index a7c7db5..35279df 100644
--- a/chrome/browser/resources/print_preview/ui/destination_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/destination_settings.ts
@@ -105,9 +105,9 @@
   accessor destination: Destination|null = null;
   accessor destinationState: DestinationState = DestinationState.INIT;
   accessor disabled: boolean = false;
-  accessor error: Error;
+  accessor error: Error|null = null;
   accessor firstLoad: boolean = false;
-  accessor state: State;
+  accessor state: State = State.NOT_READY;
   protected accessor destinationStore_: DestinationStore|null = null;
   protected accessor displayedDestinations_: Destination[] = [];
   private accessor isDialogOpen_: boolean = false;
@@ -115,7 +115,6 @@
   protected accessor pdfPrinterDisabled_: boolean = false;
   protected accessor loaded_: boolean = false;
 
-  private lastUser_: string = '';
   private tracker_: EventTracker = new EventTracker();
 
   override connectedCallback() {
diff --git a/chrome/browser/resources/print_preview/ui/dpi_settings.ts b/chrome/browser/resources/print_preview/ui/dpi_settings.ts
index e663198..1f52516 100644
--- a/chrome/browser/resources/print_preview/ui/dpi_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/dpi_settings.ts
@@ -45,10 +45,10 @@
     };
   }
 
-  accessor capability: DpiCapability|null;
+  accessor capability: DpiCapability|null = null;
   accessor disabled: boolean = false;
   protected accessor capabilityWithLabels_: DpiCapability|null = null;
-  private lastSelectedValue_: DpiOption;
+  private lastSelectedValue_: DpiOption|null = null;
 
   override connectedCallback() {
     super.connectedCallback();
diff --git a/chrome/browser/resources/print_preview/ui/header.ts b/chrome/browser/resources/print_preview/ui/header.ts
index 97e288a..dfd2334 100644
--- a/chrome/browser/resources/print_preview/ui/header.ts
+++ b/chrome/browser/resources/print_preview/ui/header.ts
@@ -47,11 +47,11 @@
   }
 
   accessor destination: Destination;
-  accessor error: Error;
-  accessor state: State;
+  accessor error: Error|null = null;
+  accessor state: State = State.NOT_READY;
   accessor managed: boolean = false;
   private accessor sheetCount_: number = 0;
-  protected accessor summary_: string|null;
+  protected accessor summary_: string|null = null;
 
   override connectedCallback() {
     super.connectedCallback();
diff --git a/chrome/browser/resources/print_preview/ui/margin_control.ts b/chrome/browser/resources/print_preview/ui/margin_control.ts
index 39d80b2b..d049aad 100644
--- a/chrome/browser/resources/print_preview/ui/margin_control.ts
+++ b/chrome/browser/resources/print_preview/ui/margin_control.ts
@@ -87,14 +87,14 @@
   }
 
   accessor disabled: boolean = false;
-  accessor side: CustomMarginsOrientation;
+  accessor side: CustomMarginsOrientation = CustomMarginsOrientation.TOP;
   accessor invalid: boolean = false;
   accessor invisible: boolean = false;
-  accessor measurementSystem: MeasurementSystem|null;
-  accessor scaleTransform: number;
+  accessor measurementSystem: MeasurementSystem|null = null;
+  accessor scaleTransform: number = 1;
   accessor translateTransform: Coordinate2d;
   accessor pageSize: Size;
-  accessor clipSize: Size|null;
+  accessor clipSize: Size|null = null;
 
   private accessor focused_: boolean = false;
   private accessor positionInPts_: number = 0;
diff --git a/chrome/browser/resources/print_preview/ui/margin_control_container.ts b/chrome/browser/resources/print_preview/ui/margin_control_container.ts
index 6eb0dc16..532d5b3 100644
--- a/chrome/browser/resources/print_preview/ui/margin_control_container.ts
+++ b/chrome/browser/resources/print_preview/ui/margin_control_container.ts
@@ -79,8 +79,8 @@
   accessor pageSize: Size;
   accessor documentMargins: Margins;
   accessor previewLoaded: boolean = false;
-  accessor measurementSystem: MeasurementSystem|null;
-  accessor state: State;
+  accessor measurementSystem: MeasurementSystem|null = null;
+  accessor state: State = State.NOT_READY;
   private accessor available_: boolean = false;
   protected accessor invisible_: boolean = true;
   protected accessor clipSize_: Size = new Size(0, 0);
diff --git a/chrome/browser/resources/print_preview/ui/margins_settings.ts b/chrome/browser/resources/print_preview/ui/margins_settings.ts
index c13bd7a..a2e31fd 100644
--- a/chrome/browser/resources/print_preview/ui/margins_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/margins_settings.ts
@@ -47,9 +47,9 @@
   }
 
   accessor disabled: boolean = false;
-  accessor state: State;
+  accessor state: State = State.NOT_READY;
   protected accessor marginsDisabled_: boolean = false;
-  private accessor pagesPerSheet_: number;
+  private accessor pagesPerSheet_: number = 1;
   private loaded_: boolean = false;
 
   override connectedCallback() {
diff --git a/chrome/browser/resources/print_preview/ui/media_size_settings.ts b/chrome/browser/resources/print_preview/ui/media_size_settings.ts
index 12a3934d..17fe2b6 100644
--- a/chrome/browser/resources/print_preview/ui/media_size_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/media_size_settings.ts
@@ -37,7 +37,7 @@
     };
   }
 
-  accessor capability: MediaSizeCapability|null;
+  accessor capability: MediaSizeCapability|null = null;
   accessor disabled: boolean = false;
   private lastSelectedValue_: string = '';
 
diff --git a/chrome/browser/resources/print_preview/ui/number_settings_section.ts b/chrome/browser/resources/print_preview/ui/number_settings_section.ts
index fae3ddc4..f8a0622 100644
--- a/chrome/browser/resources/print_preview/ui/number_settings_section.ts
+++ b/chrome/browser/resources/print_preview/ui/number_settings_section.ts
@@ -64,13 +64,13 @@
   accessor currentValue: string;
   accessor defaultValue: string;
   accessor disabled: boolean = false;
-  accessor hintMessage: string;
-  accessor inputAriaLabel: string;
-  accessor inputLabel: string;
+  accessor hintMessage: string = '';
+  accessor inputAriaLabel: string = '';
+  accessor inputLabel: string = '';
   accessor inputValid: boolean = true;
-  accessor minValue: number;
-  accessor maxValue: number;
-  protected accessor errorMessage_: string;
+  accessor minValue: number|undefined;
+  accessor maxValue: number|undefined;
+  protected accessor errorMessage_: string = '';
 
   override firstUpdated() {
     this.addEventListener('input-change', e => this.onInputChangeEvent_(e));
diff --git a/chrome/browser/resources/print_preview/ui/pages_settings.ts b/chrome/browser/resources/print_preview/ui/pages_settings.ts
index e9e4d478..0dbb6790 100644
--- a/chrome/browser/resources/print_preview/ui/pages_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/pages_settings.ts
@@ -102,7 +102,7 @@
   protected accessor hasError_: boolean = false;
   private accessor inputString_: string = '';
   private accessor pagesToPrint_: number[] = [];
-  private accessor rangesToPrint_: Range[];
+  private accessor rangesToPrint_: Range[] = [];
   private accessor selection_: PagesValue = PagesValue.ALL;
 
   /**
@@ -440,14 +440,6 @@
   }
 
   /**
-   * @return Whether to hide the hint.
-   */
-  private hintHidden_(): boolean {
-    return this.errorState_ === PagesInputErrorState.NO_ERROR ||
-        this.errorState_ === PagesInputErrorState.EMPTY;
-  }
-
-  /**
    * @return Whether to disable the custom input.
    */
   protected inputDisabled_(): boolean {
diff --git a/chrome/browser/resources/print_preview/ui/preview_area.ts b/chrome/browser/resources/print_preview/ui/preview_area.ts
index d14d59a7..4c082e77 100644
--- a/chrome/browser/resources/print_preview/ui/preview_area.ts
+++ b/chrome/browser/resources/print_preview/ui/preview_area.ts
@@ -97,12 +97,12 @@
 
   accessor destination: Destination;
   accessor documentModifiable: boolean = false;
-  accessor error: Error;
+  accessor error: Error|null = null;
   accessor margins: Margins;
-  accessor measurementSystem: MeasurementSystem|null;
+  accessor measurementSystem: MeasurementSystem|null = null;
   accessor pageSize: Size;
   accessor previewState: PreviewAreaState = PreviewAreaState.LOADING;
-  accessor state: State;
+  accessor state: State = State.NOT_READY;
   private accessor pluginLoadComplete_: boolean = false;
   private accessor documentReady_: boolean = false;
 
diff --git a/chrome/browser/resources/print_preview/ui/print_preview_search_box.ts b/chrome/browser/resources/print_preview/ui/print_preview_search_box.ts
index 743e340..88965c0 100644
--- a/chrome/browser/resources/print_preview/ui/print_preview_search_box.ts
+++ b/chrome/browser/resources/print_preview/ui/print_preview_search_box.ts
@@ -57,7 +57,7 @@
   }
 
   override accessor autofocus: boolean = false;
-  accessor searchQuery: RegExp|null;
+  accessor searchQuery: RegExp|null = null;
   private lastQuery_: string = '';
 
   override firstUpdated() {
diff --git a/chrome/browser/resources/print_preview/ui/scaling_settings.ts b/chrome/browser/resources/print_preview/ui/scaling_settings.ts
index fd0b3939..f4c0183 100644
--- a/chrome/browser/resources/print_preview/ui/scaling_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/scaling_settings.ts
@@ -64,9 +64,9 @@
   protected accessor customSelected_: boolean = false;
   protected accessor dropdownDisabled_: boolean = false;
   protected accessor inputValid_: boolean = false;
-  private accessor settingKey_: keyof Settings;
-  private accessor scalingTypeValue_: ScalingType;
-  private accessor scalingTypePdfValue_: ScalingType;
+  private accessor settingKey_: keyof Settings = 'scalingType';
+  private accessor scalingTypeValue_: ScalingType = ScalingType.DEFAULT;
+  private accessor scalingTypePdfValue_: ScalingType = ScalingType.DEFAULT;
 
   private lastValidScaling_: string = '';
 
@@ -203,10 +203,6 @@
     }
   }
 
-  private onDisabledChanged_() {
-    this.dropdownDisabled_ = this.disabled && this.inputValid_;
-  }
-
   /**
    * @return Whether the input should be disabled.
    */
diff --git a/chrome/browser/resources/print_preview/ui/settings_select.ts b/chrome/browser/resources/print_preview/ui/settings_select.ts
index 59bd002..4d37ac8 100644
--- a/chrome/browser/resources/print_preview/ui/settings_select.ts
+++ b/chrome/browser/resources/print_preview/ui/settings_select.ts
@@ -40,7 +40,7 @@
     };
   }
 
-  override accessor ariaLabel: string;
+  override accessor ariaLabel: string = '';
   accessor capability: CapabilityWithReset&{option: SelectOption[]}|null = null;
   accessor settingName: keyof Settings;
   accessor disabled: boolean = false;
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.ts b/chrome/browser/resources/print_preview/ui/sidebar.ts
index bade70e..6b30abe 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.ts
+++ b/chrome/browser/resources/print_preview/ui/sidebar.ts
@@ -123,10 +123,10 @@
   accessor destination: Destination|null = null;
   accessor destinationCapabilities_: Cdd|null = null;
   accessor destinationState: DestinationState;
-  accessor error: Error;
+  accessor error: Error|null = null;
   accessor isPdf: boolean = false;
   accessor pageCount: number;
-  accessor state: State;
+  accessor state: State = State.NOT_READY;
   protected accessor settingsAvailable_: Record<keyof Settings, boolean>;
   protected accessor controlsDisabled_: boolean = false;
   protected accessor firstLoad_: boolean = true;
diff --git a/chrome/browser/resources/privacy_sandbox/base_dialog_app.ts b/chrome/browser/resources/privacy_sandbox/base_dialog_app.ts
index e84b175d..1953f812 100644
--- a/chrome/browser/resources/privacy_sandbox/base_dialog_app.ts
+++ b/chrome/browser/resources/privacy_sandbox/base_dialog_app.ts
@@ -13,6 +13,7 @@
 
 import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 import type {CrViewManagerElement} from 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js';
+import {assert} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 
 import type {BaseDialogPageHandlerInterface} from './base_dialog.mojom-webui.js';
@@ -40,15 +41,33 @@
     return getHtml.bind(this)();
   }
 
+  private proxy_: BaseDialogBrowserProxy = BaseDialogBrowserProxy.getInstance();
   private handler_: BaseDialogPageHandlerInterface;
+  private navigateToNextStepListenerId_: number|null = null;
 
   override firstUpdated() {
-    this.handler_ = BaseDialogBrowserProxy.getInstance().handler;
+    this.handler_ = this.proxy_.handler;
     this.navigateToStep_(
             loadTimeData.getInteger('noticeIdToShow') as PrivacySandboxNotice)
         .then(() => this.resizeAndShowNativeDialog());
   }
 
+  override connectedCallback() {
+    super.connectedCallback();
+    // Once the `callbackRouter` is notified that `navigateToNextStep` is
+    // triggered, we should switch views within this dialog.
+    this.navigateToNextStepListenerId_ =
+        this.proxy_.callbackRouter.navigateToNextStep.addListener(
+            this.navigateToStep_.bind(this));
+  }
+
+  override disconnectedCallback() {
+    super.disconnectedCallback();
+    assert(this.navigateToNextStepListenerId_);
+    this.proxy_.callbackRouter.removeListener(
+        this.navigateToNextStepListenerId_);
+  }
+
   private navigateToStep_(step: PrivacySandboxNotice): Promise<void> {
     return this.$.viewManager.switchView(
         this.getNoticeId(step), 'fade-in', 'fade-out');
diff --git a/chrome/browser/resources/privacy_sandbox/base_dialog_browser_proxy.ts b/chrome/browser/resources/privacy_sandbox/base_dialog_browser_proxy.ts
index bdab7be9..dac70e4 100644
--- a/chrome/browser/resources/privacy_sandbox/base_dialog_browser_proxy.ts
+++ b/chrome/browser/resources/privacy_sandbox/base_dialog_browser_proxy.ts
@@ -3,13 +3,25 @@
 // found in the LICENSE file.
 
 import type {BaseDialogPageHandlerInterface} from './base_dialog.mojom-webui.js';
-import {BaseDialogPageHandler} from './base_dialog.mojom-webui.js';
+import {BaseDialogPageCallbackRouter, BaseDialogPageHandlerFactory, BaseDialogPageHandlerRemote} from './base_dialog.mojom-webui.js';
 
 export class BaseDialogBrowserProxy {
+  callbackRouter: BaseDialogPageCallbackRouter;
   handler: BaseDialogPageHandlerInterface;
 
+  // Creates communication pipes for both the remote and the receiver.
+  // 1. Constructs a valid PendingRemote to send messages to the
+  // `callbackRouter`.
+  // 2. Constructs a valid PendingReceiver on the existing Remote
+  // (BaseDialogPageHandlerRemote) to accept BaseDialogPageHandler interface
+  // calls.
   constructor() {
-    this.handler = BaseDialogPageHandler.getRemote();
+    this.callbackRouter = new BaseDialogPageCallbackRouter();
+    this.handler = new BaseDialogPageHandlerRemote();
+    BaseDialogPageHandlerFactory.getRemote().createPageHandler(
+        this.callbackRouter.$.bindNewPipeAndPassRemote(),
+        (this.handler as BaseDialogPageHandlerRemote)
+            .$.bindNewPipeAndPassReceiver());
   }
 
   static setInstance(proxy: BaseDialogBrowserProxy) {
diff --git a/chrome/browser/resources/privacy_sandbox/base_dialog_mixin.ts b/chrome/browser/resources/privacy_sandbox/base_dialog_mixin.ts
index 9bc8190..3b4d164 100644
--- a/chrome/browser/resources/privacy_sandbox/base_dialog_mixin.ts
+++ b/chrome/browser/resources/privacy_sandbox/base_dialog_mixin.ts
@@ -30,7 +30,6 @@
     onOptIn() {
       this.handler_.eventOccurred(
           this.notice_, PrivacySandboxNoticeEvent.kOptIn);
-      this.handler_.closeDialog();
     }
 
     onAck() {
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.css b/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.css
index 917b8447..ad382a6 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.css
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything_toolbar.css
@@ -19,6 +19,12 @@
   color: var(--color-side-panel-entry-icon);
 }
 
+@media (forced-colors: active) {
+    #play-pause {
+    --cr-icon-button-fill-color: CanvasText;
+  }
+}
+
 .audio-background-when-active-false {
   --audio-controls-background: transparent;
   --audio-controls-right-margin: 2px;
diff --git a/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc b/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc
index 9325902..b45a7f4 100644
--- a/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc
+++ b/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc
@@ -286,6 +286,12 @@
 
   EXPECT_TRUE(ocr->StatusAvailableForTesting());
   EXPECT_EQ(ocr->is_ready(), IsOcrAvailable());
+
+  base::test::TestFuture<uint32_t> max_dimension_future;
+  ocr->GetMaxImageDimension(max_dimension_future.GetCallback());
+  ASSERT_TRUE(max_dimension_future.Wait());
+  ASSERT_EQ(max_dimension_future.Get<uint32_t>(),
+            IsOcrAvailable() ? screen_ai::GetMaxDimensionForOCR() : 0);
 }
 
 IN_PROC_BROWSER_TEST_P(OpticalCharacterRecognizerTest,
@@ -299,6 +305,12 @@
   ASSERT_TRUE(future.Wait());
   ASSERT_EQ(future.Get<bool>(), IsOcrAvailable());
   ASSERT_TRUE(ocr);
+
+  base::test::TestFuture<uint32_t> max_dimension_future;
+  ocr->GetMaxImageDimension(max_dimension_future.GetCallback());
+  ASSERT_TRUE(max_dimension_future.Wait());
+  ASSERT_EQ(max_dimension_future.Get<uint32_t>(),
+            IsOcrAvailable() ? screen_ai::GetMaxDimensionForOCR() : 0);
 }
 
 IN_PROC_BROWSER_TEST_P(OpticalCharacterRecognizerTest,
diff --git a/chrome/browser/screen_ai/public/optical_character_recognizer.cc b/chrome/browser/screen_ai/public/optical_character_recognizer.cc
index 1c10fb7..6d37616 100644
--- a/chrome/browser/screen_ai/public/optical_character_recognizer.cc
+++ b/chrome/browser/screen_ai/public/optical_character_recognizer.cc
@@ -245,4 +245,16 @@
   ocr_disconnected_callback_ = std::move(callback);
 }
 
+void OpticalCharacterRecognizer::GetMaxImageDimension(
+    base::OnceCallback<void(uint32_t)> callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!is_ready()) {
+    std::move(callback).Run(0);
+    return;
+  }
+
+  MaybeConnectToOcrService();
+  (*screen_ai_annotator_)->GetMaxImageDimension(std::move(callback));
+}
+
 }  // namespace screen_ai
diff --git a/chrome/browser/screen_ai/public/optical_character_recognizer.h b/chrome/browser/screen_ai/public/optical_character_recognizer.h
index 64591b8..c5bb91a 100644
--- a/chrome/browser/screen_ai/public/optical_character_recognizer.h
+++ b/chrome/browser/screen_ai/public/optical_character_recognizer.h
@@ -8,6 +8,7 @@
 #include <optional>
 
 #include "base/memory/ref_counted_delete_on_sequence.h"
+#include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "base/sequence_checker.h"
 #include "base/threading/sequence_bound.h"
@@ -74,13 +75,15 @@
   void MaybeConnectToOcrService();
 
   // Performs OCR on the given image and returns the results as a
-  // `VisualAnnotation` struct.
+  // `VisualAnnotation` struct. Returns empty results in the callback if the
+  // service is not ready yet.
   virtual void PerformOCR(
       const SkBitmap& image,
       base::OnceCallback<void(mojom::VisualAnnotationPtr)> callback);
 
   // Performs OCR on the given image and returns the results as an accessibility
-  // tree update.
+  // tree update. Returns empty results in the callback if the service is not
+  // ready yet.
   virtual void PerformOCR(
       const SkBitmap& image,
       base::OnceCallback<void(const ui::AXTreeUpdate& tree_update)> callback);
@@ -97,6 +100,12 @@
   // to execute it on the right thread.
   void SetDisconnectedCallback(OcrDisconnectedCallback callback);
 
+  // Returns the maximum dimension for which images are processed without
+  // downsampling. This value is not expected to change after initialization of
+  // the service and is expected to be non-zero. Returns 0 in the callback if
+  // the service is not ready yet.
+  void GetMaxImageDimension(base::OnceCallback<void(uint32_t)> callback);
+
  protected:
   explicit OpticalCharacterRecognizer(Profile* profile,
                                       mojom::OcrClientType client_type);
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
index 41ccef42..107f1d3 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -458,6 +458,16 @@
      */
     void setTabHasSensitiveContent(boolean contentIsSensitive);
 
+    /** Returns the current pinned state of the tab. */
+    boolean getIsPinned();
+
+    /**
+     * Sets the pinned state of the tab.
+     *
+     * @param isPinned True if the tab is pinned.
+     */
+    void setIsPinned(boolean isPinned);
+
     /** Called when the tab is restored from the archived tab model. */
     void onTabRestoredFromArchivedTabModel();
 
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java
index e38c242..1635d8d0 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java
@@ -430,4 +430,12 @@
      * @param tab the {@link Tab} has been unarchived
      */
     default void onTabUnarchived(Tab tab) {}
+
+    /**
+     * Called when the pinned state of the tab changes.
+     *
+     * @param tab the {@link Tab} whose pinned state is changed.
+     * @param isPinned boolean indicator to represent whether tab is pinned or unpinned.
+     */
+    default void onTabPinnedStateChanged(Tab tab, boolean isPinned) {}
 }
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabState.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabState.java
index e7df901f..a3a3958 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabState.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabState.java
@@ -69,6 +69,9 @@
     // been migrated onto the new FlatBuffer format.
     public @Nullable File legacyFileToDelete;
 
+    /* Indicates whether the tab is pinned. */
+    public boolean isPinned;
+
     /** Returns true if the tab has a theme color set. */
     public boolean hasThemeColor() {
         return themeColor != UNSPECIFIED_THEME_COLOR
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java
index 972b6b0..fa53e1a9 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java
@@ -216,6 +216,12 @@
                         if (!tab.isInitialized()) return;
                         updateIsDirtyNotCheckingNtp(DirtinessState.DIRTY);
                     }
+
+                    @Override
+                    public void onTabPinnedStateChanged(Tab tab, boolean isPinned) {
+                        if (!tab.isInitialized()) return;
+                        updateIsDirty(DirtinessState.DIRTY);
+                    }
                 });
     }
 
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributesTest.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributesTest.java
index 8e1ee36..993eb56 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributesTest.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributesTest.java
@@ -519,6 +519,34 @@
     }
 
     @Test
+    public void testIsPinnedUpdates() {
+        TabStateAttributes.createForTab(mTab, TabCreationState.FROZEN_ON_RESTORE);
+        TabStateAttributes.from(mTab).addObserver(mAttributesObserver);
+        assertEquals(
+                TabStateAttributes.DirtinessState.CLEAN,
+                TabStateAttributes.from(mTab).getDirtinessState());
+
+        mTab.setIsPinned(true);
+        assertEquals(
+                TabStateAttributes.DirtinessState.DIRTY,
+                TabStateAttributes.from(mTab).getDirtinessState());
+        verify(mAttributesObserver)
+                .onTabStateDirtinessChanged(mTab, TabStateAttributes.DirtinessState.DIRTY);
+        TabStateAttributes.from(mTab).clearTabStateDirtiness();
+
+        mTab.setIsPinned(false);
+        assertEquals(
+                TabStateAttributes.DirtinessState.DIRTY,
+                TabStateAttributes.from(mTab).getDirtinessState());
+        TabStateAttributes.from(mTab).clearTabStateDirtiness();
+        verify(mAttributesObserver, times(2))
+                .onTabStateDirtinessChanged(mTab, TabStateAttributes.DirtinessState.DIRTY);
+
+        verify(mAttributesObserver, never())
+                .onTabStateDirtinessChanged(mTab, TabStateAttributes.DirtinessState.UNTIDY);
+    }
+
+    @Test
     public void testTabUnarchived() {
         TabStateAttributes.createForTab(mTab, TabCreationState.FROZEN_ON_RESTORE);
         TabStateAttributes.from(mTab).addObserver(mAttributesObserver);
diff --git a/chrome/browser/tabmodel/BUILD.gn b/chrome/browser/tabmodel/BUILD.gn
index 32a6ca5..f297c8e1 100644
--- a/chrome/browser/tabmodel/BUILD.gn
+++ b/chrome/browser/tabmodel/BUILD.gn
@@ -77,6 +77,7 @@
     "//chrome/browser/tab:java",
     "//chrome/browser/tab_group_sync:factory_java",
     "//chrome/browser/tab_group_sync:features_java",
+    "//components/browser_ui/util/android:java",
     "//components/browser_ui/widget/android:java",
     "//components/embedder_support/android:util_java",
     "//components/external_intents/android:java",
@@ -139,6 +140,7 @@
     "//chrome/browser/tabmodel:java",
     "//chrome/browser/tabwindow:java",
     "//chrome/test/android:chrome_java_unit_test_support",
+    "//components/browser_ui/util/android:java",
     "//components/browser_ui/widget/android:java",
     "//components/browser_ui/widget/android:test_support_java",
     "//components/saved_tab_groups/public:java",
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParamsUtils.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParamsUtils.java
index 868a450..a27e34b 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParamsUtils.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParamsUtils.java
@@ -8,8 +8,8 @@
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker;
-import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker.ListViewTouchInfo;
 import org.chromium.ui.util.MotionEventUtils;
 
 /** Utility to help build {@link TabClosureParams}. */
@@ -31,29 +31,28 @@
             return true;
         }
 
-        ListViewTouchInfo lastSingleTapUp = listViewTouchTracker.getLastSingleTapUp();
-        if (lastSingleTapUp == null || lastSingleTapUp.toolType.length == 0) {
-            return true;
-        }
-
-        // Allow undo as long as the last "single tap up" was *not* from a mouse.
-        return !MotionEventUtils.isMouseEvent(lastSingleTapUp.source, lastSingleTapUp.toolType[0]);
+        return shouldAllowUndo(listViewTouchTracker.getLastSingleTapUp());
     }
 
     /**
-     * Whether tab / tab group closure should allow undo, depending on the {@link MotionEvent} that
-     * triggered the closure.
+     * Whether tab / tab group closure should allow undo, depending on the {@link MotionEventInfo}
+     * that triggered the closure.
      *
-     * @param triggeringMotionEvent {@link MotionEvent} that triggered the closure.
+     * @param triggeringMotion {@link MotionEventInfo} that triggered the closure.
      * @return true if undo is allowed.
      */
-    public static boolean shouldAllowUndo(@Nullable MotionEvent triggeringMotionEvent) {
-        if (triggeringMotionEvent == null) {
+    public static boolean shouldAllowUndo(@Nullable MotionEventInfo triggeringMotion) {
+        if (triggeringMotion == null) {
+            return true;
+        }
+
+        if (triggeringMotion.toolType.length == 0) {
             return true;
         }
 
         // Allow undo as long as the triggering motion was *not* from a mouse.
-        return !MotionEventUtils.isMouseEvent(triggeringMotionEvent);
+        return !MotionEventUtils.isMouseEvent(
+                triggeringMotion.source, triggeringMotion.toolType[0]);
     }
 
     /**
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParamsUtilsUnitTest.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParamsUtilsUnitTest.java
index 845bc75..5c0d635e 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParamsUtilsUnitTest.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParamsUtilsUnitTest.java
@@ -17,9 +17,9 @@
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
 
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.list_view.FakeListViewTouchTracker;
 import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker;
-import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker.ListViewTouchInfo;
 import org.chromium.ui.util.MotionEventUtils;
 
 @RunWith(RobolectricTestRunner.class)
@@ -44,7 +44,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker fakeListViewTouchTracker = new FakeListViewTouchTracker();
         fakeListViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         createMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -62,7 +62,7 @@
         long downMotionTime = SystemClock.uptimeMillis();
         FakeListViewTouchTracker fakeListViewTouchTracker = new FakeListViewTouchTracker();
         fakeListViewTouchTracker.setLastSingleTapUpInfo(
-                ListViewTouchInfo.fromMotionEvent(
+                MotionEventInfo.fromMotionEvent(
                         createMotionEvent(
                                 downMotionTime,
                                 /* eventTime= */ downMotionTime + 50,
@@ -77,39 +77,41 @@
 
     @Test
     public void shouldAllowUndo_forTriggeringMotion_nullMotion_returnTrue() {
-        assertTrue(TabClosureParamsUtils.shouldAllowUndo((MotionEvent) null));
+        assertTrue(TabClosureParamsUtils.shouldAllowUndo((MotionEventInfo) null));
     }
 
     @Test
     public void shouldAllowUndo_forTriggeringMotion_touchScreenMotion_returnTrue() {
         long downMotionTime = SystemClock.uptimeMillis();
-        MotionEvent triggeringMotionEvent =
-                createMotionEvent(
-                        downMotionTime,
-                        /* eventTime= */ downMotionTime + 50,
-                        MotionEvent.ACTION_UP,
-                        /* x= */ 0.0f,
-                        /* y= */ 0.0f,
-                        InputDevice.SOURCE_TOUCHSCREEN,
-                        MotionEvent.TOOL_TYPE_FINGER);
+        MotionEventInfo triggeringMotion =
+                MotionEventInfo.fromMotionEvent(
+                        createMotionEvent(
+                                downMotionTime,
+                                /* eventTime= */ downMotionTime + 50,
+                                MotionEvent.ACTION_UP,
+                                /* x= */ 0.0f,
+                                /* y= */ 0.0f,
+                                InputDevice.SOURCE_TOUCHSCREEN,
+                                MotionEvent.TOOL_TYPE_FINGER));
 
-        assertTrue(TabClosureParamsUtils.shouldAllowUndo(triggeringMotionEvent));
+        assertTrue(TabClosureParamsUtils.shouldAllowUndo(triggeringMotion));
     }
 
     @Test
     public void shouldAllowUndo_forTriggeringMotion_mouseMotion_returnFalse() {
         long downMotionTime = SystemClock.uptimeMillis();
-        MotionEvent triggeringMotionEvent =
-                createMotionEvent(
-                        downMotionTime,
-                        /* eventTime= */ downMotionTime + 50,
-                        MotionEvent.ACTION_UP,
-                        /* x= */ 0.0f,
-                        /* y= */ 0.0f,
-                        InputDevice.SOURCE_MOUSE,
-                        MotionEvent.TOOL_TYPE_MOUSE);
+        MotionEventInfo triggeringMotion =
+                MotionEventInfo.fromMotionEvent(
+                        createMotionEvent(
+                                downMotionTime,
+                                /* eventTime= */ downMotionTime + 50,
+                                MotionEvent.ACTION_UP,
+                                /* x= */ 0.0f,
+                                /* y= */ 0.0f,
+                                InputDevice.SOURCE_MOUSE,
+                                MotionEvent.TOOL_TYPE_MOUSE));
 
-        assertFalse(TabClosureParamsUtils.shouldAllowUndo(triggeringMotionEvent));
+        assertFalse(TabClosureParamsUtils.shouldAllowUndo(triggeringMotion));
     }
 
     @Test
diff --git a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/FlatBufferTabStateSerializer.java b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/FlatBufferTabStateSerializer.java
index 8eeea822..ba50a7ea 100644
--- a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/FlatBufferTabStateSerializer.java
+++ b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/FlatBufferTabStateSerializer.java
@@ -94,6 +94,7 @@
         TabStateFlatBufferV1.addTabGroupId(
                 fbb, TabGroupIdToken.createTabGroupIdToken(fbb, tokenHigh, tokenLow));
         TabStateFlatBufferV1.addTabHasSensitiveContent(fbb, state.tabHasSensitiveContent);
+        TabStateFlatBufferV1.addIsPinned(fbb, state.isPinned);
         int r = TabStateFlatBufferV1.endTabStateFlatBufferV1(fbb);
         fbb.finish(r);
         return fbb.dataBuffer();
@@ -128,6 +129,7 @@
                     getLaunchTypeFromFlatBuffer(tabStateFlatBuffer.launchTypeAtCreation());
             state.themeColor = tabStateFlatBuffer.themeColor();
             state.tabHasSensitiveContent = tabStateFlatBuffer.tabHasSensitiveContent();
+            state.isPinned = tabStateFlatBuffer.isPinned();
             ByteBuffer webContentsStateBuffer =
                     tabStateFlatBuffer.webContentsStateBytesAsByteBuffer() == null
                             ? ByteBuffer.allocateDirect(0)
diff --git a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java
index b3a9dd0..98c43777 100644
--- a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java
+++ b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManager.java
@@ -475,6 +475,12 @@
                         "Failed to read tabHasSensitiveContent from tab state. "
                                 + "Assuming tabHasSensitiveContent is false");
             }
+            try {
+                tabState.isPinned = stream.readBoolean();
+            } catch (EOFException eof) {
+                tabState.isPinned = false;
+                Log.w(TAG, "Failed to read isPinned from tab state. Assuming isPinned is false");
+            }
             // If TabState was restored using legacy format and the FlatBuffer flag is on, that
             // indicates the TabState hasn't been migrated yet and should be.
             if (isMigrateStaleTabsToFlatBufferEnabled()) {
@@ -687,6 +693,7 @@
             dataOutputStream.writeLong(tokenHigh);
             dataOutputStream.writeLong(tokenLow);
             dataOutputStream.writeBoolean(state.tabHasSensitiveContent);
+            dataOutputStream.writeBoolean(state.isPinned);
             long saveTime = SystemClock.elapsedRealtime() - startTime;
             RecordHistogram.recordTimesHistogram("Tabs.TabState.SaveTime", saveTime);
             RecordHistogram.recordTimesHistogram("Tabs.TabState.SaveTime.Legacy", saveTime);
diff --git a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java
index 04009945..f00b7ed 100644
--- a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java
+++ b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java
@@ -59,6 +59,7 @@
             new Token(TAB_GROUP_ID_TOKEN_HIGH, TAB_GROUP_ID_TOKEN_LOW);
     private static final int LARGE_BYTE_BUFFER_SIZE = Integer.MAX_VALUE / 4;
     private static final boolean CONTENT_IS_SENSITIVE = true;
+    private static final boolean IS_PINNED = true;
 
     @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
 
@@ -569,6 +570,7 @@
         state.lastNavigationCommittedTimestampMillis = TIMESTAMP;
         state.tabGroupId = tabGroupId;
         state.tabHasSensitiveContent = CONTENT_IS_SENSITIVE;
+        state.isPinned = IS_PINNED;
         return state;
     }
 
@@ -600,6 +602,7 @@
         assertEquals(USER_AGENT, state.userAgent);
         assertEquals(TIMESTAMP, state.lastNavigationCommittedTimestampMillis);
         assertEquals(CONTENT_IS_SENSITIVE, state.tabHasSensitiveContent);
+        assertEquals(IS_PINNED, state.isPinned);
         if (tabGroupId == null) {
             assertNull(state.tabGroupId);
         } else {
diff --git a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/flatbuffer/tab_state_v1.fbs b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/flatbuffer/tab_state_v1.fbs
index 40d2477e..93a2997 100644
--- a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/flatbuffer/tab_state_v1.fbs
+++ b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/flatbuffer/tab_state_v1.fbs
@@ -48,5 +48,8 @@
 
     // Tab has sensitive content.
     tab_has_sensitive_content:bool;
+
+    // Tab is in pinned state.
+    is_pinned:bool;
 }
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 283c198..5b36534 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1653,9 +1653,7 @@
       "//chrome/browser/ui/webui/access_code_cast",
       "//chrome/browser/ui/webui/access_code_cast:impl",
 
-      # TODO(crbug.com/370804668i): Remove the dependency when chrome/browser/serial
-      # gets modularized.
-      "//chrome/browser/chooser_controller",
+      # No reason for this dependencies be out of alphabetical order.
       "//chrome/browser/content_extraction",
       "//chrome/browser/download",
       "//chrome/browser/enterprise/watermark:watermark_view_lib",
@@ -1723,6 +1721,8 @@
       "//chrome/browser/ui/tabs:tab_strip_model_observer",
       "//chrome/browser/ui/tabs:tab_strip_model_observer_impl",
       "//chrome/browser/ui/tabs:tabs_public",
+      "//chrome/browser/ui/tabs/alert:impl",
+      "//chrome/browser/ui/tabs/alert:tab_alert",
       "//chrome/browser/ui/toasts:impl",
       "//chrome/browser/ui/toasts:toasts",
       "//chrome/browser/ui/toasts/api:toasts",
@@ -1739,6 +1739,8 @@
       "//chrome/browser/ui/webui/commerce",
       "//chrome/browser/ui/webui/commerce:impl",
       "//chrome/browser/ui/webui/cr_components/theme_color_picker",
+      "//chrome/browser/ui/webui/customize_buttons",
+      "//chrome/browser/ui/webui/customize_buttons:impl",
       "//chrome/browser/ui/webui/new_tab_footer",
       "//chrome/browser/ui/webui/new_tab_footer:impl",
       "//chrome/browser/ui/webui/privacy_sandbox",
@@ -2231,6 +2233,7 @@
       "//chrome/browser/ash/audio",
       "//chrome/browser/ash/auth",
       "//chrome/browser/ash/base",
+      "//chrome/browser/ash/boca",
       "//chrome/browser/ash/boca/on_task",
       "//chrome/browser/ash/boot_times_recorder",
       "//chrome/browser/ash/borealis",
@@ -2754,6 +2757,7 @@
       "//chrome/browser/ash/attestation",
       "//chrome/browser/ash/audio",
       "//chrome/browser/ash/base",
+      "//chrome/browser/ash/boca",
       "//chrome/browser/ash/boca/on_task",
       "//chrome/browser/ash/boot_times_recorder",
       "//chrome/browser/ash/borealis",
diff --git a/chrome/browser/ui/android/appmenu/BUILD.gn b/chrome/browser/ui/android/appmenu/BUILD.gn
index 0d960ac..5dd2d95 100644
--- a/chrome/browser/ui/android/appmenu/BUILD.gn
+++ b/chrome/browser/ui/android/appmenu/BUILD.gn
@@ -23,6 +23,7 @@
   deps = [
     ":java_resources",
     "//chrome/browser/android/lifecycle:java",
+    "//components/browser_ui/util/android:java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_interpolator_interpolator_java",
     "//ui/android:ui_java",
diff --git a/chrome/browser/ui/android/appmenu/internal/BUILD.gn b/chrome/browser/ui/android/appmenu/internal/BUILD.gn
index bdbf99f..c707ae6 100644
--- a/chrome/browser/ui/android/appmenu/internal/BUILD.gn
+++ b/chrome/browser/ui/android/appmenu/internal/BUILD.gn
@@ -82,6 +82,7 @@
     "//chrome/browser/ui/android/appmenu:java",
     "//chrome/browser/ui/android/appmenu/test:test_support_java",
     "//chrome/test/android:chrome_java_integration_test_support",
+    "//components/browser_ui/util/android:java",
     "//components/browser_ui/widget/android:java",
     "//components/browser_ui/widget/android:test_support_java",
     "//content/public/test/android:content_java_test_support",
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
index d7a11fe..e0bc2a9b 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
@@ -20,7 +20,6 @@
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.View;
 import android.view.View.MeasureSpec;
@@ -55,6 +54,7 @@
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition;
 import org.chromium.chrome.browser.ui.appmenu.internal.R;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.chips.ChipView;
 import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter;
 import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter.HighlightParams;
@@ -453,13 +453,13 @@
     }
 
     @Override
-    public void onItemClick(PropertyModel model, @Nullable MotionEvent triggeringMotionEvent) {
+    public void onItemClick(PropertyModel model, @Nullable MotionEventInfo triggeringMotion) {
         if (!model.get(AppMenuItemProperties.ENABLED)) return;
 
         int id = model.get(AppMenuItemProperties.MENU_ITEM_ID);
         mSelectedItemBeforeDismiss = true;
         dismiss();
-        mHandler.onOptionsItemSelected(id, triggeringMotionEvent);
+        mHandler.onOptionsItemSelected(id, triggeringMotion);
     }
 
     @Override
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
index b68a090..99eae3f8 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.browser.lifecycle.ConfigurationChangedObserver;
 import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver;
 import org.chromium.chrome.browser.ui.appmenu.internal.R;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.textbubble.TextBubble;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.base.WindowAndroid;
@@ -384,19 +385,19 @@
      * Called when a menu item is selected.
      *
      * @param itemId The menu item ID.
-     * @param triggeringMotionEvent The {@link MotionEvent} that triggered the click; it is {@code
+     * @param triggeringMotion The {@link MotionEventInfo} that triggered the click; it is {@code
      *     null} if {@link MotionEvent} wasn't available when the click was detected, such as in
      *     {@link android.view.View.OnClickListener}.
      */
     @VisibleForTesting
-    void onOptionsItemSelected(int itemId, @Nullable MotionEvent triggeringMotionEvent) {
+    void onOptionsItemSelected(int itemId, @Nullable MotionEventInfo triggeringMotion) {
         if (mTestOptionsItemSelectedListener != null) {
             mTestOptionsItemSelectedListener.onResult(itemId);
             return;
         }
 
         mAppMenuDelegate.onOptionsItemSelected(
-                itemId, mDelegate.getBundleForMenuItem(itemId), triggeringMotionEvent);
+                itemId, mDelegate.getBundleForMenuItem(itemId), triggeringMotion);
     }
 
     /**
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java
index dc63914..4e89029 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java
@@ -97,9 +97,9 @@
             view.setOnTouchListener(
                     new OnPeripheralClickListener(
                             view,
-                            triggeringMotionEvent ->
+                            triggeringMotion ->
                                     model.get(AppMenuItemProperties.CLICK_HANDLER)
-                                            .onItemClick(model, triggeringMotionEvent)));
+                                            .onItemClick(model, triggeringMotion)));
             view.setOnClickListener(
                     v -> model.get(AppMenuItemProperties.CLICK_HANDLER).onItemClick(model));
         }
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderTest.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderTest.java
index 343cfcba..aa5f765 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderTest.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderTest.java
@@ -42,6 +42,7 @@
 import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler.AppMenuItemType;
 import org.chromium.chrome.browser.ui.appmenu.test.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.ui.modelutil.LayoutViewBuilder;
 import org.chromium.ui.modelutil.ModelListAdapter;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -63,7 +64,7 @@
         public PropertyModel lastLongClickedModel;
 
         @Override
-        public void onItemClick(PropertyModel model, @Nullable MotionEvent triggeringMotionEvent) {
+        public void onItemClick(PropertyModel model, @Nullable MotionEventInfo triggeringMotion) {
             onClickCallback.notifyCalled();
             lastClickedModel = model;
         }
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuDelegate.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuDelegate.java
index 29ef8ccc..0e5c0d0 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuDelegate.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuDelegate.java
@@ -5,11 +5,11 @@
 package org.chromium.chrome.browser.ui.appmenu;
 
 import android.os.Bundle;
-import android.view.MotionEvent;
 
 import androidx.annotation.Nullable;
 
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 
 class TestAppMenuDelegate implements AppMenuDelegate {
     public final CallbackHelper itemSelectedCallbackHelper = new CallbackHelper();
@@ -17,9 +17,7 @@
 
     @Override
     public boolean onOptionsItemSelected(
-            int itemId,
-            @Nullable Bundle menuItemData,
-            @Nullable MotionEvent triggeringMotionEvent) {
+            int itemId, @Nullable Bundle menuItemData, @Nullable MotionEventInfo triggeringMotion) {
         lastSelectedItemId = itemId;
         itemSelectedCallbackHelper.notifyCalled();
         return true;
diff --git a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuClickHandler.java b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuClickHandler.java
index 35bb6ea..227028e7 100644
--- a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuClickHandler.java
+++ b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuClickHandler.java
@@ -9,6 +9,7 @@
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** Interface to handle clicks and long-clicks on menu items. */
@@ -21,21 +22,21 @@
      * <p>The default implementation works for most cases, so it's not recommended to override it
      * unless you are sure.
      *
-     * @see #onItemClick(PropertyModel, MotionEvent)
+     * @see #onItemClick(PropertyModel, MotionEventInfo)
      */
     default void onItemClick(PropertyModel model) {
-        onItemClick(model, /* triggeringMotionEvent= */ null);
+        onItemClick(model, /* triggeringMotion= */ null);
     }
 
     /**
      * Handles clicks on the AppMenu popup.
      *
      * @param model The {@link PropertyModel} of the clicked menu item.
-     * @param triggeringMotionEvent The {@link MotionEvent} that triggered the click; it is {@code
+     * @param triggeringMotion The {@link MotionEventInfo} that triggered the click; it is {@code
      *     null} if {@link MotionEvent} wasn't available when the click was detected, such as in
      *     {@link android.view.View.OnClickListener}.
      */
-    void onItemClick(PropertyModel model, @Nullable MotionEvent triggeringMotionEvent);
+    void onItemClick(PropertyModel model, @Nullable MotionEventInfo triggeringMotion);
 
     /**
      * Handles long clicks on image buttons on the AppMenu popup.
diff --git a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuDelegate.java b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuDelegate.java
index 05bd22a..1e70d2a 100644
--- a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuDelegate.java
+++ b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuDelegate.java
@@ -10,6 +10,7 @@
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 
 /** A delegate to handle menu item selection. */
 @NullMarked
@@ -21,10 +22,10 @@
      * <p>The default implementation works for most cases, so it's not recommended to override it
      * unless you are sure.
      *
-     * @see #onOptionsItemSelected(int, Bundle, MotionEvent)
+     * @see #onOptionsItemSelected(int, Bundle, MotionEventInfo)
      */
     default boolean onOptionsItemSelected(int itemId, @Nullable Bundle menuItemData) {
-        return onOptionsItemSelected(itemId, menuItemData, /* triggeringMotionEvent= */ null);
+        return onOptionsItemSelected(itemId, menuItemData, /* triggeringMotion= */ null);
     }
 
     /**
@@ -33,12 +34,12 @@
      *
      * @param itemId The id of the menu item that was selected.
      * @param menuItemData Extra data associated with the menu item. May be null.
-     * @param triggeringMotionEvent The {@link MotionEvent} that triggered the click; it is {@code
-     *     null} if {@link MotionEvent} wasn't available when the click was detected, such as in
-     *     {@link android.view.View.OnClickListener}.
+     * @param triggeringMotion The {@link MotionEvent} that triggered the click; it is {@code null}
+     *     if {@link MotionEvent} wasn't available when the click was detected, such as in {@link
+     *     android.view.View.OnClickListener}.
      */
     boolean onOptionsItemSelected(
-            int itemId, @Nullable Bundle menuItemData, @Nullable MotionEvent triggeringMotionEvent);
+            int itemId, @Nullable Bundle menuItemData, @Nullable MotionEventInfo triggeringMotion);
 
     /**
      * @return {@link AppMenuPropertiesDelegate} instance that the {@link AppMenuHandlerImpl} should
diff --git a/chrome/browser/ui/android/appmenu/test/BUILD.gn b/chrome/browser/ui/android/appmenu/test/BUILD.gn
index 3f124f4..2b3d2a8d 100644
--- a/chrome/browser/ui/android/appmenu/test/BUILD.gn
+++ b/chrome/browser/ui/android/appmenu/test/BUILD.gn
@@ -17,6 +17,7 @@
     "//base:base_java_test_support",
     "//chrome/browser/ui/android/appmenu:java",
     "//chrome/browser/ui/android/appmenu/internal:java",
+    "//components/browser_ui/util/android:java",
     "//components/browser_ui/widget/android:java_resources",
     "//ui/android:ui_no_recycler_view_java",
   ]
diff --git a/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java b/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
index d947c43..14b76bc 100644
--- a/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
+++ b/chrome/browser/ui/android/appmenu/test/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTestSupport.java
@@ -5,12 +5,12 @@
 package org.chromium.chrome.browser.ui.appmenu;
 
 import android.os.Bundle;
-import android.view.MotionEvent;
 import android.view.View;
 import android.widget.ListView;
 
 import org.chromium.base.Callback;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -40,16 +40,16 @@
     public static void onOptionsItemSelected(AppMenuCoordinator coordinator, int itemId) {
         ((AppMenuCoordinatorImpl) coordinator)
                 .getAppMenuHandlerImplForTesting()
-                .onOptionsItemSelected(itemId, /* triggeringMotionEvent= */ null);
+                .onOptionsItemSelected(itemId, /* triggeringMotion= */ null);
     }
 
     /**
      * Simulates a click on a menu item.
      *
-     * @see #callOnItemClick(AppMenuCoordinator, int, MotionEvent)
+     * @see #callOnItemClick(AppMenuCoordinator, int, MotionEventInfo)
      */
     public static void callOnItemClick(AppMenuCoordinator coordinator, int menuItemId) {
-        callOnItemClick(coordinator, menuItemId, /* triggeringMotionEvent= */ null);
+        callOnItemClick(coordinator, menuItemId, /* triggeringMotion= */ null);
     }
 
     /**
@@ -57,13 +57,13 @@
      *
      * @param coordinator The {@link AppMenuCoordinator} associated with the app menu being tested.
      * @param menuItemId The id of the menu item to click.
-     * @param triggeringMotionEvent The {@link MotionEvent} that triggered the click. See {@link
-     *     AppMenuClickHandler#onItemClick(PropertyModel, MotionEvent)}.
+     * @param triggeringMotion The {@link MotionEventInfo} that triggered the click. See {@link
+     *     AppMenuClickHandler#onItemClick(PropertyModel, MotionEventInfo)}.
      */
     public static void callOnItemClick(
             AppMenuCoordinator coordinator,
             int menuItemId,
-            @Nullable MotionEvent triggeringMotionEvent) {
+            @Nullable MotionEventInfo triggeringMotion) {
         PropertyModel model =
                 ((AppMenuCoordinatorImpl) coordinator)
                         .getAppMenuHandlerImplForTesting()
@@ -73,7 +73,7 @@
         ((AppMenuCoordinatorImpl) coordinator)
                 .getAppMenuHandlerImplForTesting()
                 .getAppMenu()
-                .onItemClick(model, triggeringMotionEvent);
+                .onItemClick(model, triggeringMotion);
     }
 
     /**
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index c49cf5e..e109521 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -1093,6 +1093,9 @@
     private void setProfile(Profile profile) {
         if (profile == null || !mNativeInitialized) return;
 
+        mUrlCoordinator.setUrlBarHintText(
+                SearchEngineUtils.getForProfile(mProfileSupplier.get()).getSearchBoxHintText());
+
         assumeNonNull(mOmniboxPrerender);
         mOmniboxPrerender.initializeForProfile(profile);
         mSearchEngineUtils = SearchEngineUtils.getForProfile(profile);
@@ -1379,6 +1382,8 @@
     @Override
     public void onTemplateURLServiceChanged() {
         sLastCachedIsLensOnOmniboxEnabled = Boolean.valueOf(isLensEnabled(LensEntryPoint.OMNIBOX));
+        mUrlCoordinator.setUrlBarHintText(
+                SearchEngineUtils.getForProfile(mProfileSupplier.get()).getSearchBoxHintText());
     }
 
     // OmniboxStub implementation.
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
index d838875..2a71e8a 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
@@ -16,6 +16,7 @@
 
 import org.chromium.base.TraceEvent;
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.components.omnibox.OmniboxFeatures;
 
 /** A location bar implementation specific for smaller/phone screens. */
 @NullMarked
@@ -98,7 +99,9 @@
     }
 
     int getOffsetOfFirstVisibleFocusedView() {
-        if (mLocationBarDataProvider.isIncognito() && mStatusView.getVisibility() != View.GONE) {
+        if (!OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled()
+                && mLocationBarDataProvider.isIncognito()
+                && mStatusView.getVisibility() != View.GONE) {
             return mStatusView.getMeasuredWidth();
         }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/NewTabPageDelegate.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/NewTabPageDelegate.java
index 601da30..2ad7d9a 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/NewTabPageDelegate.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/NewTabPageDelegate.java
@@ -34,6 +34,14 @@
     }
 
     /**
+     * Whether the incognito version of the NewTabPage {@link
+     * org.chromium.chrome.browser.ntp.IncognitoNewTabPage} is currently visible.
+     */
+    default boolean isIncognitoNewTabPageCurrentlyVisible() {
+        return false;
+    }
+
+    /**
      * @return Whether the location bar is shown in the NTP.
      */
     default boolean isLocationBarShown() {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
index d26f09d..e0c45f7 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
@@ -11,7 +11,6 @@
 import android.view.inputmethod.InputMethodManager;
 
 import androidx.annotation.IntDef;
-import androidx.annotation.StringRes;
 
 import org.chromium.base.Callback;
 import org.chromium.build.annotations.NullMarked;
@@ -370,9 +369,9 @@
     }
 
     /**
-     * @see UrlBarMediator#setUrlBarHintText(int)
+     * @see UrlBarMediator#setUrlBarHintText(String)
      */
-    public void setUrlBarHintText(@StringRes int hintTextRes) {
+    public void setUrlBarHintText(String hintTextRes) {
         mMediator.setUrlBarHintText(hintTextRes);
     }
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
index b25deb0b..0f296ff 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
@@ -12,7 +12,6 @@
 import android.view.View;
 
 import androidx.annotation.ColorInt;
-import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
@@ -416,8 +415,8 @@
     }
 
     /** Sets the search box hint text. */
-    void setUrlBarHintText(@StringRes int hintTextRes) {
-        mModel.set(UrlBarProperties.HINT_TEXT, hintTextRes);
+    void setUrlBarHintText(String hintText) {
+        mModel.set(UrlBarProperties.HINT_TEXT, hintText);
     }
 
     void setShowOriginOnly(boolean showOriginOnly) {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
index 6659a43..2032084 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediatorUnitTest.java
@@ -46,11 +46,13 @@
     @Mock Callback<String> mAnotherUrlTextMockListener;
     @Mock Callback<Boolean> mFocusChangeCallback;
 
+    Context mContext;
     PropertyModel mModel;
     UrlBarMediator mMediator;
 
     @Before
     public void setUp() {
+        mContext = ContextUtils.getApplicationContext();
         mModel = new PropertyModel(UrlBarProperties.ALL_KEYS);
         mMediator =
                 new UrlBarMediator(
@@ -323,11 +325,10 @@
 
     @Test
     public void setUrlBarHintText() {
-        mMediator.setUrlBarHintText(R.string.hub_search_empty_hint);
-        Assert.assertEquals(R.string.hub_search_empty_hint, mModel.get(UrlBarProperties.HINT_TEXT));
-        mMediator.setUrlBarHintText(R.string.hub_search_empty_hint_incognito);
-        Assert.assertEquals(
-                R.string.hub_search_empty_hint_incognito, mModel.get(UrlBarProperties.HINT_TEXT));
+        mMediator.setUrlBarHintText("Hint 1");
+        Assert.assertEquals("Hint 1", mModel.get(UrlBarProperties.HINT_TEXT));
+        mMediator.setUrlBarHintText("Incognito Hint");
+        Assert.assertEquals("Incognito Hint", mModel.get(UrlBarProperties.HINT_TEXT));
     }
 
     @Test
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
index deaddaf..bb72256 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
@@ -178,8 +178,9 @@
     public static final WritableObjectPropertyKey<View.OnLongClickListener> LONG_CLICK_LISTENER =
             new WritableObjectPropertyKey<>();
 
-    /** Specifies the resource ID for the url bar hint text. */
-    public static final WritableIntPropertyKey HINT_TEXT = new WritableIntPropertyKey();
+    /** Specifies the url bar hint text. */
+    public static final WritableObjectPropertyKey<String> HINT_TEXT =
+            new WritableObjectPropertyKey<>();
 
     public static final PropertyKey[] ALL_KEYS =
             new PropertyKey[] {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
index feb7595..3cdbed2 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
@@ -136,7 +136,7 @@
         } else if (UrlBarProperties.LONG_CLICK_LISTENER.equals(propertyKey)) {
             view.setOnLongClickListener(model.get(UrlBarProperties.LONG_CLICK_LISTENER));
         } else if (UrlBarProperties.HINT_TEXT.equals(propertyKey)) {
-            view.setHint(view.getContext().getString(model.get(UrlBarProperties.HINT_TEXT)));
+            view.setHint(model.get(UrlBarProperties.HINT_TEXT));
         }
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java
index 8a339b9..98976e5 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java
@@ -190,11 +190,10 @@
     @Test
     @SmallTest
     public void testSetHintText() {
-        mModel.set(HINT_TEXT, R.string.hub_search_empty_hint);
-        Assert.assertEquals(mActivity.getString(R.string.hub_search_empty_hint), mUrlBar.getHint());
-        mModel.set(HINT_TEXT, R.string.hub_search_empty_hint_incognito);
-        Assert.assertEquals(
-                mActivity.getString(R.string.hub_search_empty_hint_incognito), mUrlBar.getHint());
+        mModel.set(HINT_TEXT, "Hint Text");
+        Assert.assertEquals("Hint Text", mUrlBar.getHint());
+        mModel.set(HINT_TEXT, "Different Hint Text");
+        Assert.assertEquals("Different Hint Text", mUrlBar.getHint());
     }
 
     @Test
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
index 91b45dbb..1e77215 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
@@ -33,6 +33,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
+import org.chromium.components.omnibox.OmniboxFeatures;
 import org.chromium.components.permissions.PermissionDialogController;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.ui.base.WindowAndroid;
@@ -391,7 +392,8 @@
      */
     public void populateFadeAnimation(
             List<Animator> animators, long startDelayMs, long durationMs, float targetAlpha) {
-        if (mLocationBarDataProvider.isIncognitoBranded()) {
+        if (mLocationBarDataProvider.isIncognitoBranded()
+                && !OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled()) {
             Animator animator =
                     PropertyModelAnimatorFactory.ofFloat(
                                     mModel, StatusProperties.ALPHA, targetAlpha)
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index fbea20b..777133c3 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -73,7 +73,10 @@
     private final Supplier<Profile> mProfileSupplier;
     private final @Nullable Supplier<MerchantTrustSignalsCoordinator>
             mMerchantTrustSignalsCoordinatorSupplier;
-    private final boolean mAlwaysShowDseIconOnNtp;
+    // When the parity update is enabled, we want to:
+    // 1. Always show the DSE logo on the regular and incognito NTP
+    // 2. Remove the incognito badge.
+    private final boolean mParityUpdateEnabled;
     private boolean mUrlHasFocus;
     private boolean mVerboseStatusSpaceAvailable;
     private boolean mPageIsPaintPreview;
@@ -169,13 +172,17 @@
 
         mIsTablet = isTablet;
         mShowStatusIconWhenUrlFocused = mIsTablet;
-        mAlwaysShowDseIconOnNtp = OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled();
+        mParityUpdateEnabled = OmniboxFeatures.sOmniboxMobileParityUpdate.isEnabled();
+        if (mParityUpdateEnabled) {
+            mModel.set(StatusProperties.INCOGNITO_BADGE_VISIBLE, false);
+        }
 
         mPermissionDialogController = permissionDialogController;
         mPermissionDialogController.addObserver(this);
 
         updateColorTheme();
-        setStatusIconShown(/* show= */ !mLocationBarDataProvider.isIncognitoBranded());
+        setStatusIconShown(
+                /* show= */ mParityUpdateEnabled || !mLocationBarDataProvider.isIncognitoBranded());
         updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
@@ -320,12 +327,13 @@
         // This logic doesn't apply to tablets.
         if (mIsTablet) return;
 
-        boolean shouldShowLogo = !mLocationBarDataProvider.isIncognitoBranded();
+        boolean shouldShowLogo =
+                mParityUpdateEnabled || !mLocationBarDataProvider.isIncognitoBranded();
         setShowIconsWhenUrlFocused(shouldShowLogo);
         if (!shouldShowLogo) return;
 
         if (mProfileSupplier.hasValue() && isNtpVisible()) {
-            setStatusIconShown(mAlwaysShowDseIconOnNtp || mUrlHasFocus || mUrlFocusPercent > 0);
+            setStatusIconShown(mParityUpdateEnabled || mUrlHasFocus || mUrlFocusPercent > 0);
         } else {
             setStatusIconShown(true);
         }
@@ -356,7 +364,7 @@
         updateStatusVisibility();
 
         // Only fade the animation on the new tab page.
-        if (mProfileSupplier.hasValue() && isNtpVisible() && !mAlwaysShowDseIconOnNtp) {
+        if (mProfileSupplier.hasValue() && isNtpVisible() && !mParityUpdateEnabled) {
             setStatusIconAlpha(percent);
         } else {
             setStatusIconAlpha(1f);
@@ -466,6 +474,13 @@
                 && mLocationBarDataProvider.getNewTabPageDelegate().isCurrentlyVisible();
     }
 
+    private boolean isIncognitoNtpVisible() {
+        return mLocationBarDataProvider.getNewTabPageDelegate() != null
+                && mLocationBarDataProvider
+                        .getNewTabPageDelegate()
+                        .isIncognitoNewTabPageCurrentlyVisible();
+    }
+
     /**
      * Update selection of icon presented on the location bar.
      *
@@ -561,7 +576,7 @@
             return false;
         }
 
-        if (mLocationBarDataProvider.isIncognitoBranded()) {
+        if (mLocationBarDataProvider.isIncognitoBranded() && !mParityUpdateEnabled) {
             return false;
         }
 
@@ -569,8 +584,8 @@
             return true;
         }
 
-        return (mAlwaysShowDseIconOnNtp || mUrlHasFocus || mUrlFocusPercent > 0)
-                && isNtpVisible()
+        return (mParityUpdateEnabled || mUrlHasFocus || mUrlFocusPercent > 0)
+                && (isNtpVisible() || isIncognitoNtpVisible())
                 && mProfileSupplier.hasValue();
     }
 
@@ -634,11 +649,13 @@
     }
 
     public void onIncognitoStateChanged() {
-        boolean incognitoBadgeVisible = mLocationBarDataProvider.isIncognitoBranded();
-        mModel.set(StatusProperties.INCOGNITO_BADGE_VISIBLE, incognitoBadgeVisible);
-        mModel.set(StatusProperties.STATUS_ICON_RESOURCE, null);
-        setStatusIconAlpha(1f);
-        setStatusIconShown(false);
+        if (!mParityUpdateEnabled) {
+            boolean incognitoBadgeVisible = mLocationBarDataProvider.isIncognitoBranded();
+            mModel.set(StatusProperties.INCOGNITO_BADGE_VISIBLE, incognitoBadgeVisible);
+            mModel.set(StatusProperties.STATUS_ICON_RESOURCE, null);
+            setStatusIconAlpha(1f);
+            setStatusIconShown(false);
+        }
     }
 
     // PermissionDialogController.Observer interface
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
index ceb9a6a02..5529fd9 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
@@ -265,6 +265,7 @@
 
     @Test
     @SmallTest
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void searchEngineLogoTablet() {
         setupStatusMediator(/* isTablet= */ true);
         mMediator.setUrlHasFocus(true);
@@ -347,6 +348,7 @@
 
     @Test
     @SmallTest
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void searchEngineLogo_incognitoStateChanged() {
         mMediator.onIncognitoStateChanged();
 
@@ -439,6 +441,7 @@
 
     @Test
     @SmallTest
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void testIncognitoStateChange_goingToIncognito() {
         mMediator.setShowIconsWhenUrlFocused(true);
 
@@ -450,6 +453,7 @@
 
     @Test
     @SmallTest
+    @DisableFeatures(OmniboxFeatureList.OMNIBOX_MOBILE_PARITY_UPDATE)
     public void testIncognitoStateChange_backFromIncognito() {
         mMediator.setShowIconsWhenUrlFocused(true);
 
@@ -463,6 +467,30 @@
 
     @Test
     @SmallTest
+    public void testIncognitoStateChange() {
+        mMediator.setShowIconsWhenUrlFocused(true);
+        doReturn(true).when(mLocationBarDataProvider).isIncognito();
+        mMediator.onIncognitoStateChanged();
+        Assert.assertEquals(true, mModel.get(StatusProperties.SHOW_STATUS_ICON));
+        Assert.assertFalse(mModel.get(StatusProperties.INCOGNITO_BADGE_VISIBLE));
+
+        doReturn(true).when(mNewTabPageDelegate).isIncognitoNewTabPageCurrentlyVisible();
+        mMediator.updateLocationBarIcon(IconTransitionType.CROSSFADE);
+
+        Assert.assertEquals(
+                R.drawable.ic_logo_googleg_20dp,
+                mModel.get(StatusProperties.STATUS_ICON_RESOURCE).getIconResForTesting());
+        Assert.assertFalse(mModel.get(StatusProperties.INCOGNITO_BADGE_VISIBLE));
+
+        mMediator.setUrlHasFocus(true);
+        Assert.assertEquals(
+                R.drawable.ic_logo_googleg_20dp,
+                mModel.get(StatusProperties.STATUS_ICON_RESOURCE).getIconResForTesting());
+        Assert.assertFalse(mModel.get(StatusProperties.INCOGNITO_BADGE_VISIBLE));
+    }
+
+    @Test
+    @SmallTest
     public void testStatusText() {
         mMediator.setUnfocusedLocationBarWidth(10);
         mMediator.updateVerboseStatus(ConnectionSecurityLevel.SECURE, true, true);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryStateSupplier.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryStateSupplier.java
index 4df6bf4..878d4ba 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryStateSupplier.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/KeyboardAccessoryStateSupplier.java
@@ -26,11 +26,15 @@
     private final ObservableSupplier<ManualFillingComponent> mManualFillingComponentSupplier;
     private final Callback<Integer> mInsetChangeCallback = this::set;
     private @Nullable ManualFillingComponent mManualFillingComponent;
+    private final ObservableSupplierImpl<Boolean> mIsSheetShowingSupplier =
+            new ObservableSupplierImpl<>(false);
+    private final View mView;
 
     public KeyboardAccessoryStateSupplier(
-            ObservableSupplier<ManualFillingComponent> manualFillingComponentSupplier) {
+            ObservableSupplier<ManualFillingComponent> manualFillingComponentSupplier, View view) {
         super(0);
         mManualFillingComponentSupplier = manualFillingComponentSupplier;
+        mView = view;
         ManualFillingComponent manualFillingComponent =
                 mManualFillingComponentSupplier.addObserver(mManualFillingAvailableCallback);
         if (manualFillingComponent != null) {
@@ -38,6 +42,14 @@
         }
     }
 
+    @Override
+    public void set(Integer object) {
+        super.set(object);
+        mIsSheetShowingSupplier.set(
+                mManualFillingComponent != null
+                        && mManualFillingComponent.isFillingViewShown(mView));
+    }
+
     private void onManualFillingComponentAvailable(ManualFillingComponent manualFillingComponent) {
         mManualFillingComponent = manualFillingComponent;
         mManualFillingComponent.getBottomInsetSupplier().addObserver(mInsetChangeCallback);
@@ -50,13 +62,7 @@
         }
     }
 
-    /**
-     * {@link ManualFillingComponent#isFillingViewShown(View)}
-     *
-     * @param view A {@link View} that is used to find the window root.
-     */
-    public boolean isSheetShowing(View view) {
-        if (mManualFillingComponent == null) return false;
-        return mManualFillingComponent.isFillingViewShown(view);
+    public ObservableSupplierImpl<Boolean> getIsSheetShowingSupplier() {
+        return mIsSheetShowingSupplier;
     }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
index 46bbe6c..54bc931 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
@@ -11,6 +11,7 @@
 import android.view.ViewGroup.LayoutParams;
 import android.widget.FrameLayout;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.view.WindowInsetsAnimationCompat;
 import androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat;
@@ -20,6 +21,7 @@
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsSizer;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.Observer;
@@ -31,6 +33,8 @@
 import org.chromium.ui.KeyboardVisibilityDelegate.KeyboardVisibilityListener;
 import org.chromium.ui.base.ViewUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.function.BooleanSupplier;
 
@@ -42,6 +46,60 @@
 @NullMarked
 public class MiniOriginBarController implements Observer {
 
+    @IntDef({
+        MiniOriginState.NOT_READY,
+        MiniOriginState.READY,
+        MiniOriginState.ANIMATING,
+        MiniOriginState.SHOWING,
+        MiniOriginState.SHOWING_WITH_ACCESSORY_SHEET,
+        MiniOriginState.SUPPRESSED_BY_CLICK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface MiniOriginState {
+        // The mini origin bar isn't showing and can't show even if a form field is focused.
+        int NOT_READY = 0;
+        // The mini origin bar isn't showing but can show if the soft keyboard shows.
+        int READY = 1;
+        // The mini origin bar is animating to either show or hide itself.
+        int ANIMATING = 2;
+        // The mini origin bar is showing at its fully minimized size.
+        int SHOWING = 3;
+        // The mini origin bar is showing at its fully minimized size and is stacked on top of a
+        // keyboard accessory sheet.
+        int SHOWING_WITH_ACCESSORY_SHEET = 4;
+        // The mini origin bar has been suppressed by a user click and should not show again until a
+        // new "session" begins.
+        int SUPPRESSED_BY_CLICK = 5;
+    }
+
+    @IntDef({
+        MiniOriginEvent.KEYBOARD_APPEARED,
+        MiniOriginEvent.KEYBOARD_DISAPPEARED,
+        MiniOriginEvent.KEYBOARD_ANIMATION_PREPARED,
+        MiniOriginEvent.KEYBOARD_ANIMATION_ENDED,
+        MiniOriginEvent.FORM_FIELD_GAINED_FOCUS,
+        MiniOriginEvent.FORM_FIELD_LOST_FOCUS,
+        MiniOriginEvent.CONTROLS_POSITION_BECAME_TOP,
+        MiniOriginEvent.CONTROLS_POSITION_BECAME_BOTTOM,
+        MiniOriginEvent.ORIGIN_BAR_CLICKED,
+        MiniOriginEvent.ACCESSORY_SHEET_APPEARED,
+        MiniOriginEvent.ACCESSORY_SHEET_DISAPPEARED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface MiniOriginEvent {
+        int KEYBOARD_APPEARED = 0;
+        int KEYBOARD_DISAPPEARED = 1;
+        int KEYBOARD_ANIMATION_PREPARED = 2;
+        int KEYBOARD_ANIMATION_ENDED = 3;
+        int FORM_FIELD_GAINED_FOCUS = 4;
+        int FORM_FIELD_LOST_FOCUS = 5;
+        int CONTROLS_POSITION_BECAME_TOP = 6;
+        int CONTROLS_POSITION_BECAME_BOTTOM = 7;
+        int ORIGIN_BAR_CLICKED = 8;
+        int ACCESSORY_SHEET_APPEARED = 9;
+        int ACCESSORY_SHEET_DISAPPEARED = 10;
+    }
+
     private final LocationBar mLocationBar;
     private final ObservableSupplier<Boolean> mIsFormFieldFocusedSupplier;
     private final KeyboardVisibilityDelegate mKeyboardVisibilityDelegate;
@@ -51,14 +109,12 @@
     private final ControlContainer mControlContainer;
     private final ObservableSupplierImpl<Boolean> mSuppressToolbarSceneLayerSupplier;
     private final BrowserControlsSizer mBrowserControlsSizer;
-    private final BooleanSupplier mIsKeyboardAccessorySheetShowing;
+    private final ObservableSupplier<Boolean> mIsKeyboardAccessorySheetShowing;
     private final MiniOriginWindowInsetsAnimationListener mWindowInsetsAnimationListener;
-    private boolean mShowMiniOriginBar;
+    private @MiniOriginState int mMiniOriginBarState = MiniOriginState.NOT_READY;
     private FrameLayout.LayoutParams mDefaultLocationBarLayoutParams;
-    private boolean mOriginBarClickedInSession;
     private final TouchEventObserver mTouchEventObserver;
     private final int mDefaultLocationBarRightPadding;
-    private boolean mFormFieldFocusChanged;
 
     /**
      * @param locationBar LocationBar instance used to change the presentation of e.g. the UrlBar
@@ -80,7 +136,7 @@
             BrowserControlsSizer browserControlsSizer,
             InsetObserver insetObserver,
             ObservableSupplierImpl<Integer> controlContainerTranslationSupplier,
-            BooleanSupplier isKeyboardAccessorySheetShowing) {
+            ObservableSupplier<Boolean> isKeyboardAccessorySheetShowing) {
         mLocationBar = locationBar;
         mIsFormFieldFocusedSupplier = isFormFieldFocusedSupplier;
         mKeyboardVisibilityDelegate = keyboardVisibilityDelegate;
@@ -98,75 +154,106 @@
                         keyboardVisibilityDelegate,
                         (ViewGroup) mLocationBar.getContainerView(),
                         controlContainerTranslationSupplier,
-                        () -> mFormFieldFocusChanged = false,
+                        () -> updateMiniOriginBarState(MiniOriginEvent.KEYBOARD_ANIMATION_PREPARED),
+                        () -> updateMiniOriginBarState(MiniOriginEvent.KEYBOARD_ANIMATION_ENDED),
                         this::waitingForImeAnimationToStart);
         insetObserver.addWindowInsetsAnimationListener(mWindowInsetsAnimationListener);
 
         mIsFormFieldFocusedObserver =
                 (focused) -> {
-                    mFormFieldFocusChanged = true;
-                    updateMiniOriginBarState();
+                    updateMiniOriginBarState(
+                            focused
+                                    ? MiniOriginEvent.FORM_FIELD_GAINED_FOCUS
+                                    : MiniOriginEvent.FORM_FIELD_LOST_FOCUS);
                 };
-        mKeyboardVisibilityObserver = (showing) -> updateMiniOriginBarState();
+        mKeyboardVisibilityObserver =
+                (showing) ->
+                        updateMiniOriginBarState(
+                                showing
+                                        ? MiniOriginEvent.KEYBOARD_APPEARED
+                                        : MiniOriginEvent.KEYBOARD_DISAPPEARED);
 
         mIsFormFieldFocusedSupplier.addObserver(mIsFormFieldFocusedObserver);
         mKeyboardVisibilityDelegate.addKeyboardVisibilityListener(mKeyboardVisibilityObserver);
 
         mTouchEventObserver =
                 e -> {
-                    if (!mShowMiniOriginBar) return false;
+                    if (mMiniOriginBarState != MiniOriginState.SHOWING
+                            && mMiniOriginBarState != MiniOriginState.SHOWING_WITH_ACCESSORY_SHEET
+                            && mMiniOriginBarState != MiniOriginState.ANIMATING) return false;
+                    // Suppress all clicks during animation as they are 1) unlikely to be
+                    // intentional and 2) difficult to cleanly handle.
+                    if (mMiniOriginBarState == MiniOriginState.ANIMATING) return true;
                     boolean isDownEvent = e.getActionMasked() == MotionEvent.ACTION_DOWN;
-                    mOriginBarClickedInSession = isDownEvent;
-                    updateMiniOriginBarState();
+                    updateMiniOriginBarState(MiniOriginEvent.ORIGIN_BAR_CLICKED);
                     return isDownEvent;
                 };
         controlContainer.addTouchEventObserver(mTouchEventObserver);
+
+        mIsKeyboardAccessorySheetShowing.addObserver(
+                (showing) ->
+                        updateMiniOriginBarState(
+                                showing
+                                        ? MiniOriginEvent.ACCESSORY_SHEET_APPEARED
+                                        : MiniOriginEvent.ACCESSORY_SHEET_DISAPPEARED));
     }
 
-    private void updateMiniOriginBarState() {
-        boolean isFormFieldFocused = mIsFormFieldFocusedSupplier.get();
-        boolean isKeyboardVisible =
-                mKeyboardVisibilityDelegate.isKeyboardShowing(
-                        mContext, mControlContainer.getView());
-        boolean isToolbarBottomAnchored =
-                mBrowserControlsSizer.getControlsPosition() == ControlsPosition.BOTTOM;
-        if (!isFormFieldFocused || !isKeyboardVisible) mOriginBarClickedInSession = false;
+    private void updateMiniOriginBarState(@MiniOriginEvent int event) {
+        @MiniOriginState int newMiniOriginState = getNewMiniOriginState(event);
+        if (newMiniOriginState == mMiniOriginBarState) return;
 
-        boolean showMiniOriginBar =
-                !mOriginBarClickedInSession
-                        && isToolbarBottomAnchored
-                        && isFormFieldFocused
-                        && isKeyboardVisible;
-        if (showMiniOriginBar == mShowMiniOriginBar) return;
+        transitionToNewState(newMiniOriginState);
+    }
 
-        if (showMiniOriginBar) {
+    private void transitionToNewState(@MiniOriginState int newMiniOriginState) {
+        // TODO: Add animation-specific setup code so that the text scales and translates to/from
+        // the correct start/end points.
+        boolean isChangingVisibility =
+                isMiniOriginBarVisibleForState(newMiniOriginState)
+                        != isMiniOriginBarVisibleForState(mMiniOriginBarState);
+        mMiniOriginBarState = newMiniOriginState;
+        if (!isChangingVisibility) return;
+
+        if (isMiniOriginBarVisibleForState(newMiniOriginState)) {
+            // Cache the location bar's layout params now, since we are about to mutate them.
             mDefaultLocationBarLayoutParams =
                     (FrameLayout.LayoutParams) mLocationBar.getContainerView().getLayoutParams();
+            showMiniOriginBar();
+        } else {
+            hideMiniOriginBar();
         }
+    }
 
-        mShowMiniOriginBar = showMiniOriginBar;
-        mLocationBar.setShowOriginOnly(mShowMiniOriginBar);
-        mLocationBar.setUrlBarUsesSmallText(mShowMiniOriginBar);
-        mLocationBar.setHideStatusIconForSecureOrigins(mShowMiniOriginBar);
-        mSuppressToolbarSceneLayerSupplier.set(mShowMiniOriginBar);
-        mControlContainer.toggleLocationBarOnlyMode(mShowMiniOriginBar);
+    private boolean isMiniOriginBarVisibleForState(@MiniOriginState int miniOriginBarState) {
+        return switch (miniOriginBarState) {
+            case MiniOriginState.NOT_READY,
+                    MiniOriginState.READY,
+                    MiniOriginState.SUPPRESSED_BY_CLICK -> false;
+            case MiniOriginState.ANIMATING,
+                    MiniOriginState.SHOWING,
+                    MiniOriginState.SHOWING_WITH_ACCESSORY_SHEET -> true;
+            default -> throw new IllegalStateException(
+                    "Unexpected mini origin state: " + miniOriginBarState);
+        };
+    }
+
+    private void showMiniOriginBar() {
+        mLocationBar.setShowOriginOnly(true);
+        mLocationBar.setUrlBarUsesSmallText(true);
+        mLocationBar.setHideStatusIconForSecureOrigins(true);
+        mSuppressToolbarSceneLayerSupplier.set(true);
+        mControlContainer.toggleLocationBarOnlyMode(true);
 
         int newControlContainerHeight =
-                showMiniOriginBar
-                        ? mContext.getResources()
-                                .getDimensionPixelSize(R.dimen.mini_origin_bar_height)
-                        : LayoutParams.WRAP_CONTENT;
+                mContext.getResources().getDimensionPixelSize(R.dimen.mini_origin_bar_height);
         mControlContainer.mutateLayoutParams().height = newControlContainerHeight;
-
         var minifiedLayoutParams =
                 new FrameLayout.LayoutParams(
                         LayoutParams.WRAP_CONTENT, newControlContainerHeight, Gravity.CENTER);
 
         var locationBarView = mLocationBar.getContainerView();
-        var locationBarLayoutParams =
-                mShowMiniOriginBar ? minifiedLayoutParams : mDefaultLocationBarLayoutParams;
-        locationBarView.setLayoutParams(locationBarLayoutParams);
-        int locationBarRightPadding = mShowMiniOriginBar ? 0 : mDefaultLocationBarRightPadding;
+        locationBarView.setLayoutParams(minifiedLayoutParams);
+        int locationBarRightPadding = 0;
         locationBarView.setPadding(
                 locationBarView.getPaddingLeft(),
                 locationBarView.getPaddingTop(),
@@ -174,6 +261,24 @@
                 locationBarView.getPaddingBottom());
     }
 
+    private void hideMiniOriginBar() {
+        mLocationBar.setShowOriginOnly(false);
+        mLocationBar.setUrlBarUsesSmallText(false);
+        mLocationBar.setHideStatusIconForSecureOrigins(false);
+        mSuppressToolbarSceneLayerSupplier.set(false);
+        mControlContainer.toggleLocationBarOnlyMode(false);
+
+        mControlContainer.mutateLayoutParams().height = LayoutParams.WRAP_CONTENT;
+
+        var locationBarView = mLocationBar.getContainerView();
+        locationBarView.setLayoutParams(mDefaultLocationBarLayoutParams);
+        locationBarView.setPadding(
+                locationBarView.getPaddingLeft(),
+                locationBarView.getPaddingTop(),
+                mDefaultLocationBarRightPadding,
+                locationBarView.getPaddingBottom());
+    }
+
     public void destroy() {
         mKeyboardVisibilityDelegate.removeKeyboardVisibilityListener(mKeyboardVisibilityObserver);
         mIsFormFieldFocusedSupplier.removeObserver(mIsFormFieldFocusedObserver);
@@ -182,12 +287,107 @@
 
     @Override
     public void onControlsPositionChanged(int controlsPosition) {
-        updateMiniOriginBarState();
+        updateMiniOriginBarState(
+                controlsPosition == ControlsPosition.BOTTOM
+                        ? MiniOriginEvent.CONTROLS_POSITION_BECAME_BOTTOM
+                        : MiniOriginEvent.CONTROLS_POSITION_BECAME_TOP);
     }
 
-    @VisibleForTesting
-    boolean waitingForImeAnimationToStart() {
-        return mFormFieldFocusChanged && !mIsKeyboardAccessorySheetShowing.getAsBoolean();
+    private boolean waitingForImeAnimationToStart() {
+        return mMiniOriginBarState == MiniOriginState.READY
+                || mMiniOriginBarState == MiniOriginState.SHOWING;
+    }
+
+    /**
+     * Gets the next state of the mini origin bar considering current state and an event. This logic
+     * assumes that: 1. Form field focus changes precede the associated keyboard visibility change
+     * 2. Keyboard animation prepare events precede the associated keyboard visibility change 3.
+     * Multiple keyboard visibility change events can fire in the course of a single animation
+     */
+    private @MiniOriginState int getNewMiniOriginState(@MiniOriginEvent int miniOriginEvent) {
+        switch (mMiniOriginBarState) {
+            case MiniOriginState.NOT_READY -> {
+                if (mIsFormFieldFocusedSupplier.get()
+                        && mBrowserControlsSizer.getControlsPosition() == ControlsPosition.BOTTOM) {
+                    return isKeyboardShowing() ? MiniOriginState.SHOWING : MiniOriginState.READY;
+                }
+                return MiniOriginState.NOT_READY;
+            }
+            case MiniOriginState.READY -> {
+                return switch (miniOriginEvent) {
+                    case MiniOriginEvent.FORM_FIELD_LOST_FOCUS,
+                            MiniOriginEvent.CONTROLS_POSITION_BECAME_TOP -> MiniOriginState
+                            .NOT_READY;
+                    case MiniOriginEvent.KEYBOARD_ANIMATION_PREPARED -> MiniOriginState.ANIMATING;
+                    case MiniOriginEvent.KEYBOARD_APPEARED ->
+                    // Skip our animation if we get a keyboard appearance event before the animation
+                    // prepare signal.
+                    MiniOriginState.SHOWING;
+                    default -> MiniOriginState.READY;
+                };
+            }
+            case MiniOriginState.ANIMATING -> {
+                return switch (miniOriginEvent) {
+                    case MiniOriginEvent.CONTROLS_POSITION_BECAME_TOP -> MiniOriginState.NOT_READY;
+                    case MiniOriginEvent.KEYBOARD_ANIMATION_ENDED -> isKeyboardShowing()
+                            ? MiniOriginState.SHOWING
+                            : MiniOriginState.READY;
+                    case MiniOriginEvent.ACCESSORY_SHEET_APPEARED -> MiniOriginState
+                            .SHOWING_WITH_ACCESSORY_SHEET;
+                    default -> MiniOriginState.ANIMATING;
+                };
+            }
+            case MiniOriginState.SHOWING -> {
+                return switch (miniOriginEvent) {
+                    case MiniOriginEvent.ACCESSORY_SHEET_APPEARED -> MiniOriginState
+                            .SHOWING_WITH_ACCESSORY_SHEET;
+                    case MiniOriginEvent.CONTROLS_POSITION_BECAME_TOP -> MiniOriginState.NOT_READY;
+                    case MiniOriginEvent.KEYBOARD_ANIMATION_PREPARED -> MiniOriginState.ANIMATING;
+                    case MiniOriginEvent.KEYBOARD_DISAPPEARED ->
+                    // Skip our animation if we get a keyboard disappearance event before the
+                    // animation prepare signal.
+                    MiniOriginState.READY;
+                    case MiniOriginEvent.ORIGIN_BAR_CLICKED -> MiniOriginState.SUPPRESSED_BY_CLICK;
+                    default -> MiniOriginState.SHOWING;
+                };
+            }
+            case MiniOriginState.SHOWING_WITH_ACCESSORY_SHEET -> {
+                return switch (miniOriginEvent) {
+                    case MiniOriginEvent.CONTROLS_POSITION_BECAME_TOP,
+                            MiniOriginEvent.FORM_FIELD_LOST_FOCUS -> MiniOriginState.NOT_READY;
+                    case MiniOriginEvent.ORIGIN_BAR_CLICKED -> MiniOriginState.SUPPRESSED_BY_CLICK;
+                    case MiniOriginEvent.ACCESSORY_SHEET_DISAPPEARED -> MiniOriginState.SHOWING;
+                        // We don't animate from this state because the accessory sheet is in the
+                        // way.
+                    default -> MiniOriginState.SHOWING_WITH_ACCESSORY_SHEET;
+                };
+            }
+            case MiniOriginState.SUPPRESSED_BY_CLICK -> {
+                return switch (miniOriginEvent) {
+                    case MiniOriginEvent.CONTROLS_POSITION_BECAME_TOP,
+                            MiniOriginEvent.FORM_FIELD_LOST_FOCUS -> MiniOriginState.NOT_READY;
+                    case MiniOriginEvent.KEYBOARD_DISAPPEARED -> MiniOriginState.READY;
+                        // We don't animate from this state because the accessory sheet is in the
+                        // way.
+                    default -> MiniOriginState.SUPPRESSED_BY_CLICK;
+                };
+            }
+        }
+        assert false : "Unrecognized initial mini origin state";
+        return mMiniOriginBarState;
+    }
+
+    @MiniOriginState
+    int getCurrentStateForTesting() {
+        return mMiniOriginBarState;
+    }
+
+    MiniOriginWindowInsetsAnimationListener getAnimationListenerForTesting() {
+        return mWindowInsetsAnimationListener;
+    }
+
+    private boolean isKeyboardShowing() {
+        return mKeyboardVisibilityDelegate.isKeyboardShowing(mContext, mControlContainer.getView());
     }
 
     @VisibleForTesting
@@ -199,34 +399,44 @@
         private final ViewGroup mContainerView;
         private final ObservableSupplierImpl<Integer> mTranslationSupplier;
         private final Context mContext;
-        private final Runnable mAnimationStartedSignal;
+        private final Runnable mOnAnimationPreparedSignal;
+        private final Runnable mAnimationEndedSignal;
         private final BooleanSupplier mWaitingForAnimation;
+        private @Nullable WindowInsetsAnimationCompat mAnimation;
 
         MiniOriginWindowInsetsAnimationListener(
                 KeyboardVisibilityDelegate keyboardVisibilityDelegate,
                 ViewGroup containerView,
                 ObservableSupplierImpl<Integer> translationSupplier,
-                Runnable animationStartedSignal,
+                Runnable animationPreparedSignal,
+                Runnable animationEndedSignal,
                 BooleanSupplier waitingForAnimation) {
             mKeyboardVisibilityDelegate = keyboardVisibilityDelegate;
             mContainerView = containerView;
             mTranslationSupplier = translationSupplier;
             mContext = containerView.getContext();
-            mAnimationStartedSignal = animationStartedSignal;
+            mOnAnimationPreparedSignal = animationPreparedSignal;
+            mAnimationEndedSignal = animationEndedSignal;
             mWaitingForAnimation = waitingForAnimation;
         }
 
         @Override
-        public void onPrepare(WindowInsetsAnimationCompat animation) {}
-
-        @Override
-        public void onStart(WindowInsetsAnimationCompat animation, BoundsCompat bounds) {
+        public void onPrepare(WindowInsetsAnimationCompat animation) {
             if (!mWaitingForAnimation.getAsBoolean()
                     || ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) == 0)) {
                 return;
             }
 
-            mAnimationStartedSignal.run();
+            mAnimation = animation;
+            mOnAnimationPreparedSignal.run();
+        }
+
+        @Override
+        public void onStart(WindowInsetsAnimationCompat animation, BoundsCompat bounds) {
+            if (animation != mAnimation) {
+                return;
+            }
+
             // Prevent clipping so that the mini origin bar can draw in bounds allocated for the
             // keyboard; we will prevent overlap by syncing our translation to its movement in
             // onProgress.
@@ -255,6 +465,8 @@
             ViewUtils.setAncestorsShouldClipChildren(mContainerView, true, ViewGroup.NO_ID);
             ViewUtils.setAncestorsShouldClipToPadding(mContainerView, true, ViewGroup.NO_ID);
             mTranslationSupplier.set(0);
+            mAnimationEndedSignal.run();
+            mAnimation = null;
         }
     }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java
index 6d18da7..d2664ef 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java
@@ -24,6 +24,7 @@
 import androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat;
 import androidx.core.view.WindowInsetsCompat;
 
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -41,12 +42,12 @@
 import org.chromium.chrome.browser.browser_controls.BrowserControlsSizer;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition;
 import org.chromium.chrome.browser.omnibox.LocationBar;
+import org.chromium.chrome.browser.toolbar.MiniOriginBarController.MiniOriginState;
 import org.chromium.chrome.browser.toolbar.MiniOriginBarController.MiniOriginWindowInsetsAnimationListener;
 import org.chromium.components.browser_ui.widget.TouchEventObserver;
 import org.chromium.ui.InsetObserver;
 
 import java.util.Collections;
-import java.util.function.BooleanSupplier;
 
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
@@ -75,14 +76,13 @@
             new ObservableSupplierImpl<>(false);
     ObservableSupplierImpl<Integer> mControlContainerTranslationSupplier =
             new ObservableSupplierImpl<>(0);
-    private boolean mIsSheetShowing;
-    BooleanSupplier mIsKeyboardAccessorySheetShowing = () -> mIsSheetShowing;
+    private final ObservableSupplierImpl<Boolean> mIsKeyboardAccessorySheetShowing =
+            new ObservableSupplierImpl<>(false);
 
     private final WindowInsetsAnimationCompat mImeAnimation =
             new WindowInsetsAnimationCompat(WindowInsetsCompat.Type.ime(), null, 160);
     private final WindowInsetsAnimationCompat mNonImeAnimation =
             new WindowInsetsAnimationCompat(WindowInsetsCompat.Type.systemBars(), null, 160);
-    private boolean mAnimationStarted;
 
     @Before
     public void setUp() {
@@ -108,14 +108,14 @@
 
     @Test
     public void testUpdateMiniOriginBarState() {
+        doReturn(ControlsPosition.BOTTOM).when(mBrowserControlsSizer).getControlsPosition();
+        mMiniOriginBarController.onControlsPositionChanged(ControlsPosition.BOTTOM);
+
         mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
         verify(mLocationBar, never()).setShowOriginOnly(anyBoolean());
 
         mKeyboardVisibilityDelegate.setVisibilityForTests(true);
-        verify(mLocationBar, never()).setShowOriginOnly(anyBoolean());
 
-        doReturn(ControlsPosition.BOTTOM).when(mBrowserControlsSizer).getControlsPosition();
-        mMiniOriginBarController.onControlsPositionChanged(ControlsPosition.BOTTOM);
         verify(mLocationBar).setShowOriginOnly(true);
         verify(mLocationBar).setUrlBarUsesSmallText(true);
         verify(mLocationBarView).setLayoutParams(mLayoutParamsCaptor.capture());
@@ -124,12 +124,16 @@
         assertEquals(
                 mContext.getResources().getDimensionPixelSize(R.dimen.mini_origin_bar_height),
                 mLayoutParamsCaptor.getValue().height);
+        Assert.assertEquals(
+                MiniOriginState.SHOWING, mMiniOriginBarController.getCurrentStateForTesting());
 
         mKeyboardVisibilityDelegate.setVisibilityForTests(false);
         verify(mLocationBar).setShowOriginOnly(false);
         verify(mLocationBar).setUrlBarUsesSmallText(false);
         assertEquals(LayoutParams.WRAP_CONTENT, mControlContainerLayoutParams.height);
         assertEquals(Gravity.TOP, mLocationBarLayoutParams.gravity);
+        Assert.assertEquals(
+                MiniOriginState.READY, mMiniOriginBarController.getCurrentStateForTesting());
     }
 
     @Test
@@ -149,72 +153,89 @@
         TouchEventObserver observer = mTouchEventObserverCaptor.getValue();
         assertFalse(observer.onInterceptTouchEvent(clickEvent));
 
-        mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
-        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
         doReturn(ControlsPosition.BOTTOM).when(mBrowserControlsSizer).getControlsPosition();
         mMiniOriginBarController.onControlsPositionChanged(ControlsPosition.BOTTOM);
+
+        mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
         verify(mLocationBar).setShowOriginOnly(true);
+        Assert.assertEquals(
+                MiniOriginState.SHOWING, mMiniOriginBarController.getCurrentStateForTesting());
 
         assertTrue(observer.onInterceptTouchEvent(clickEvent));
+        Assert.assertEquals(
+                MiniOriginState.SUPPRESSED_BY_CLICK,
+                mMiniOriginBarController.getCurrentStateForTesting());
 
         verify(mLocationBar).setShowOriginOnly(false);
         verify(mLocationBar).setUrlBarUsesSmallText(false);
         assertFalse(observer.onInterceptTouchEvent(clickEvent));
 
-        mIsFormFieldFocused.onNodeAttributeUpdated(false, false);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(false);
         // The effect of the click only persists until the "session" ends, e.g. via un-focusing a
         // form or hiding the keyboard.
-        mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
+        Assert.assertEquals(
+                MiniOriginState.SHOWING, mMiniOriginBarController.getCurrentStateForTesting());
         assertTrue(observer.onInterceptTouchEvent(clickEvent));
     }
 
     @Test
     public void testAnimateWithKeyboard_notReadyForAnimation() {
+        doReturn(ControlsPosition.BOTTOM).when(mBrowserControlsSizer).getControlsPosition();
+        mMiniOriginBarController.onControlsPositionChanged(ControlsPosition.BOTTOM);
         final MiniOriginWindowInsetsAnimationListener animationListener =
-                new MiniOriginWindowInsetsAnimationListener(
-                        mKeyboardVisibilityDelegate,
-                        mLocationBarView,
-                        mControlContainerTranslationSupplier,
-                        () -> mAnimationStarted = true,
-                        mMiniOriginBarController::waitingForImeAnimationToStart);
+                mMiniOriginBarController.getAnimationListenerForTesting();
         final BoundsCompat bounds = new BoundsCompat(Insets.NONE, Insets.of(0, 0, 0, 40));
 
+        animationListener.onPrepare(mImeAnimation);
         animationListener.onStart(mImeAnimation, bounds);
-        assertFalse(mAnimationStarted);
+        Assert.assertEquals(
+                MiniOriginState.NOT_READY, mMiniOriginBarController.getCurrentStateForTesting());
 
         mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
+        animationListener.onPrepare(mImeAnimation);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
         animationListener.onStart(mImeAnimation, bounds);
-        assertTrue(mAnimationStarted);
+        Assert.assertEquals(
+                MiniOriginState.ANIMATING, mMiniOriginBarController.getCurrentStateForTesting());
 
         animationListener.onEnd(mImeAnimation);
-        mAnimationStarted = false;
-        mIsSheetShowing = true;
-        animationListener.onStart(mImeAnimation, bounds);
-        assertFalse(mAnimationStarted);
+        Assert.assertEquals(
+                MiniOriginState.SHOWING, mMiniOriginBarController.getCurrentStateForTesting());
+        mIsKeyboardAccessorySheetShowing.set(true);
+        Assert.assertEquals(
+                MiniOriginState.SHOWING_WITH_ACCESSORY_SHEET,
+                mMiniOriginBarController.getCurrentStateForTesting());
 
-        mIsSheetShowing = false;
-        animationListener.onStart(mNonImeAnimation, bounds);
-        assertFalse(mAnimationStarted);
+        animationListener.onPrepare(mImeAnimation);
+        Assert.assertEquals(
+                MiniOriginState.SHOWING_WITH_ACCESSORY_SHEET,
+                mMiniOriginBarController.getCurrentStateForTesting());
+
+        mIsKeyboardAccessorySheetShowing.set(false);
+        Assert.assertEquals(
+                MiniOriginState.SHOWING, mMiniOriginBarController.getCurrentStateForTesting());
     }
 
     @Test
     public void testAnimateWithKeyboard() {
+        doReturn(ControlsPosition.BOTTOM).when(mBrowserControlsSizer).getControlsPosition();
+        mMiniOriginBarController.onControlsPositionChanged(ControlsPosition.BOTTOM);
+
         final MiniOriginWindowInsetsAnimationListener animationListener =
-                new MiniOriginWindowInsetsAnimationListener(
-                        mKeyboardVisibilityDelegate,
-                        mLocationBarView,
-                        mControlContainerTranslationSupplier,
-                        () -> mAnimationStarted = true,
-                        mMiniOriginBarController::waitingForImeAnimationToStart);
+                mMiniOriginBarController.getAnimationListenerForTesting();
         final int finalKeyboardHeight = 100;
         final BoundsCompat bounds =
                 new BoundsCompat(Insets.NONE, Insets.of(0, 0, 0, finalKeyboardHeight));
 
         mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
-        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
 
+        animationListener.onPrepare(mImeAnimation);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
         animationListener.onStart(mImeAnimation, bounds);
-        assertTrue(mAnimationStarted);
+        Assert.assertEquals(
+                MiniOriginState.ANIMATING, mMiniOriginBarController.getCurrentStateForTesting());
 
         int currentKeyboardHeight = 10;
         WindowInsetsCompat insets =
@@ -254,14 +275,17 @@
 
         animationListener.onEnd(mImeAnimation);
         assertEquals(0, (int) mControlContainerTranslationSupplier.get());
+        Assert.assertEquals(
+                MiniOriginState.SHOWING, mMiniOriginBarController.getCurrentStateForTesting());
 
         // Simulate hiding the keyboard
         mIsFormFieldFocused.onNodeAttributeUpdated(false, false);
         mKeyboardVisibilityDelegate.setVisibilityForTests(false);
-        mAnimationStarted = false;
 
+        animationListener.onPrepare(mImeAnimation);
         animationListener.onStart(mImeAnimation, bounds);
-        assertTrue(mAnimationStarted);
+        Assert.assertEquals(
+                MiniOriginState.ANIMATING, mMiniOriginBarController.getCurrentStateForTesting());
 
         animationListener.onProgress(insets, Collections.singletonList(mImeAnimation));
         assertEquals(-currentKeyboardHeight, (int) mControlContainerTranslationSupplier.get());
@@ -277,6 +301,8 @@
         assertEquals(-currentKeyboardHeight, (int) mControlContainerTranslationSupplier.get());
 
         animationListener.onEnd(mImeAnimation);
+        Assert.assertEquals(
+                MiniOriginState.READY, mMiniOriginBarController.getCurrentStateForTesting());
         assertEquals(0, (int) mControlContainerTranslationSupplier.get());
     }
 
@@ -285,31 +311,36 @@
         // Predictive back gestures can cause an IME hide animation to run but finish with the IME
         // still showing if the gesture is cancelled.
 
+        doReturn(ControlsPosition.BOTTOM).when(mBrowserControlsSizer).getControlsPosition();
+        mMiniOriginBarController.onControlsPositionChanged(ControlsPosition.BOTTOM);
         final MiniOriginWindowInsetsAnimationListener animationListener =
-                new MiniOriginWindowInsetsAnimationListener(
-                        mKeyboardVisibilityDelegate,
-                        mLocationBarView,
-                        mControlContainerTranslationSupplier,
-                        () -> mAnimationStarted = true,
-                        mMiniOriginBarController::waitingForImeAnimationToStart);
+                mMiniOriginBarController.getAnimationListenerForTesting();
+
         final int finalKeyboardHeight = 100;
         final BoundsCompat bounds =
                 new BoundsCompat(Insets.NONE, Insets.of(0, 0, 0, finalKeyboardHeight));
 
         mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
-        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
 
+        animationListener.onPrepare(mImeAnimation);
         animationListener.onStart(mImeAnimation, bounds);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
+        Assert.assertEquals(
+                MiniOriginState.ANIMATING, mMiniOriginBarController.getCurrentStateForTesting());
+
         animationListener.onEnd(mImeAnimation);
         assertEquals(0, (int) mControlContainerTranslationSupplier.get());
+        Assert.assertEquals(
+                MiniOriginState.SHOWING, mMiniOriginBarController.getCurrentStateForTesting());
 
         // Simulate the beginning of an animation hiding the keyboard
 
-        mKeyboardVisibilityDelegate.setVisibilityForTests(false);
-        mAnimationStarted = false;
+        animationListener.onPrepare(mImeAnimation);
+        Assert.assertEquals(
+                MiniOriginState.ANIMATING, mMiniOriginBarController.getCurrentStateForTesting());
 
+        mKeyboardVisibilityDelegate.setVisibilityForTests(false);
         animationListener.onStart(mImeAnimation, bounds);
-        assertTrue(mAnimationStarted);
 
         int currentKeyboardHeight = 90;
         WindowInsetsCompat insets =
@@ -324,6 +355,8 @@
         mKeyboardVisibilityDelegate.setVisibilityForTests(true);
         animationListener.onEnd(mImeAnimation);
         assertEquals(0, (int) mControlContainerTranslationSupplier.get());
+        Assert.assertEquals(
+                MiniOriginState.SHOWING, mMiniOriginBarController.getCurrentStateForTesting());
     }
 
     // show again, start, finish showing (predictive back)
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
index ac94f84..1250a8c4 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
@@ -6,6 +6,7 @@
 
 import android.graphics.Rect;
 import android.os.Build;
+import android.os.SystemClock;
 import android.view.ViewStub;
 import android.widget.ImageButton;
 
@@ -14,6 +15,7 @@
 import androidx.core.graphics.Insets;
 
 import org.chromium.base.Callback;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.blink.mojom.DisplayMode;
@@ -68,6 +70,7 @@
     private final Callback<Integer> mOnUnoccludedWidthCallback;
     private final ObservableSupplierImpl<Boolean> mControlsEnabledSupplier;
     private final TokenHolder mDisabledControlsHolder;
+    private long mLastButtonVisibilityChangeTime;
 
     /**
      * Creates an instance of {@link WebAppHeaderLayoutCoordinator}.
@@ -104,6 +107,7 @@
         mOnUnoccludedWidthCallback = this::onUnoccludedWidthChanged;
         mMinUIControlsMinWidthPx = 0;
         mAppHeaderUnoccludedWidthPx = 0;
+        mLastButtonVisibilityChangeTime = 0;
 
         final var appHeaderState = desktopWindowStateManager.getAppHeaderState();
         if (appHeaderState != null) {
@@ -139,7 +143,8 @@
                         this::collectNonDraggableAreas,
                         mThemeColorProvider,
                         headerMinHeight,
-                        headerButtonHeight);
+                        headerButtonHeight,
+                        mDisplayMode);
         PropertyModelChangeProcessor.create(model, mView, WebAppHeaderLayoutViewBinder::bind);
 
         mMediator.getUnoccludedWidthSupplier().addObserver(mOnUnoccludedWidthCallback);
@@ -183,18 +188,33 @@
     }
 
     private void onUnoccludedWidthChanged(int newUnoccludedWidthPx) {
+        boolean wasShowingButtons = mAppHeaderUnoccludedWidthPx >= mMinUIControlsMinWidthPx;
         mAppHeaderUnoccludedWidthPx = newUnoccludedWidthPx;
-        updateButtonVisibility();
-    }
-
-    private void updateButtonVisibility() {
         boolean showButtons = mAppHeaderUnoccludedWidthPx >= mMinUIControlsMinWidthPx;
+
+        if (wasShowingButtons == showButtons) return;
+
         if (mReloadButtonCoordinator != null) {
             mReloadButtonCoordinator.setVisibility(showButtons);
         }
         if (mBackButtonCoordinator != null) {
             mBackButtonCoordinator.setVisibility(showButtons);
         }
+        logControlsVisibilityChange(wasShowingButtons);
+    }
+
+    private void logControlsVisibilityChange(boolean wasShowingButtons) {
+        if (mLastButtonVisibilityChangeTime != 0) {
+            long duration = SystemClock.elapsedRealtime() - mLastButtonVisibilityChangeTime;
+            if (wasShowingButtons) {
+                RecordHistogram.recordLongTimesHistogram(
+                        "CustomTabs.WebAppHeader.ControlsShownTime", duration);
+            } else {
+                RecordHistogram.recordLongTimesHistogram(
+                        "CustomTabs.WebAppHeader.ControlsHiddenTime", duration);
+            }
+        }
+        mLastButtonVisibilityChangeTime = SystemClock.elapsedRealtime();
     }
 
     private void updateControlsEnabledState() {
@@ -242,6 +262,8 @@
      * called.
      */
     public void destroy() {
+        logControlsVisibilityChange(mAppHeaderUnoccludedWidthPx >= mMinUIControlsMinWidthPx);
+
         mDesktopWindowStateManager.removeObserver(this);
 
         if (mView != null) {
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java
index d3b9e41..57a56f8 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java
@@ -41,6 +41,7 @@
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.blink.mojom.DisplayMode;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.intents.WebappExtras;
@@ -238,6 +239,20 @@
         verifyHeaderContainsNonDraggableAreas(List.of(new Rect(0, 0, 0, 0)));
     }
 
+    private void testDisplayModeUMA(@DisplayMode.EnumType int displayMode) {
+        var histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord("CustomTabs.WebAppHeader.DisplayMode", displayMode)
+                        .build();
+
+        setupDesktopWindowing(/* isInDesktopWindow= */ true);
+        setupDisplayMode(displayMode);
+        setupTab(/* isLoading= */ false, /* canGoBack= */ true);
+        createCoordinator();
+
+        histogramWatcher.assertExpected();
+    }
+
     @Test
     public void testInitNoAppHeaderState_shouldNotInitCoordinator() {
         when(mDesktopWindowStateManager.getAppHeaderState()).thenReturn(null);
@@ -387,4 +402,85 @@
         mCoordinator.releaseDisabledControlsToken(firstToken);
         verifyControlsEnabledState(false);
     }
+
+    @Test
+    public void testControlsVisibilityChangeUMA() {
+        // Emulate minimizing window.
+        int flexibleAreaWidth = MIN_HEADER_WIDTH_DP - 1;
+        setupDesktopWindowing(
+                new Rect(0, 0, LEFT_INSET + flexibleAreaWidth + RIGHT_INSET, SCREEN_HEIGHT),
+                new Rect(LEFT_INSET, 0, LEFT_INSET + flexibleAreaWidth, SYS_APP_HEADER_HEIGHT),
+                /* isInDesktopWindow= */ true);
+        setupDisplayMode(DisplayMode.MINIMAL_UI);
+        setupTab(/* isLoading= */ false, /* canGoBack= */ true);
+        createCoordinator();
+
+        mShadowLooper.idle();
+
+        var histogramWatcher =
+                HistogramWatcher.newBuilder()
+                        .expectAnyRecord("CustomTabs.WebAppHeader.ControlsShownTime")
+                        .expectAnyRecord("CustomTabs.WebAppHeader.ControlsHiddenTime")
+                        .build();
+
+        // Emulate maximizing window.
+        setupDesktopWindowing(/* isInDesktopWindow= */ true);
+        notifyHeaderStateChanged();
+        mShadowLooper.idle();
+
+        // Emulate minimizing window.
+        setupDesktopWindowing(
+                new Rect(0, 0, LEFT_INSET + flexibleAreaWidth + RIGHT_INSET, SCREEN_HEIGHT),
+                new Rect(LEFT_INSET, 0, LEFT_INSET + flexibleAreaWidth, SYS_APP_HEADER_HEIGHT),
+                /* isInDesktopWindow= */ true);
+        notifyHeaderStateChanged();
+        mShadowLooper.idle();
+
+        // Emulate maximizing window.
+        setupDesktopWindowing(/* isInDesktopWindow= */ true);
+        notifyHeaderStateChanged();
+        mShadowLooper.idle();
+
+        histogramWatcher.assertExpected();
+    }
+
+    @Test
+    public void testDisplayModeBrowserUMA() {
+        testDisplayModeUMA(DisplayMode.BROWSER);
+    }
+
+    @Test
+    public void testDisplayModeMinimalUIUMA() {
+        testDisplayModeUMA(DisplayMode.MINIMAL_UI);
+    }
+
+    @Test
+    public void testDisplayModeStandaloneUMA() {
+        testDisplayModeUMA(DisplayMode.STANDALONE);
+    }
+
+    @Test
+    public void testDisplayModeFullscreenUMA() {
+        testDisplayModeUMA(DisplayMode.FULLSCREEN);
+    }
+
+    @Test
+    public void testDisplayModeWindowControlsOverlayUMA() {
+        testDisplayModeUMA(DisplayMode.WINDOW_CONTROLS_OVERLAY);
+    }
+
+    @Test
+    public void testDisplayModeTabbedUMA() {
+        testDisplayModeUMA(DisplayMode.TABBED);
+    }
+
+    @Test
+    public void testDisplayModeBorderlessUMA() {
+        testDisplayModeUMA(DisplayMode.BORDERLESS);
+    }
+
+    @Test
+    public void testDisplayModePiPUMA() {
+        testDisplayModeUMA(DisplayMode.PICTURE_IN_PICTURE);
+    }
 }
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java
index 3c7d6e2b..e532608 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java
@@ -11,9 +11,11 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.ResettersForTesting;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.Supplier;
+import org.chromium.blink.mojom.DisplayMode;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.tab.Tab;
@@ -55,8 +57,10 @@
     private final Callback<Boolean> mScrimVisibilityObserver;
     private @Nullable Callback<Integer> mOnButtonBottomInsetChanged;
     private int mButtonBottomInset;
+    private final @DisplayMode.EnumType int mDisplayMode;
 
     private int mDisabledControlsToken = TokenHolder.INVALID_TOKEN;
+    private boolean mIsFirstAppHeaderStateUpdate = true;
 
     /**
      * Constructs the instance of {@link WebAppHeaderLayoutMediator}.
@@ -79,7 +83,8 @@
             Supplier<List<Rect>> nonDraggableAreasSupplier,
             ThemeColorProvider themeColorProvider,
             int webAppHeaderMinHeightFromResources,
-            int headerButtonHeight) {
+            int headerButtonHeight,
+            int displayMode) {
         mThemeColorProvider = themeColorProvider;
         mWebAppMinHeaderHeight = webAppHeaderMinHeightFromResources;
         mHeaderDelegate = headerDelegate;
@@ -87,6 +92,7 @@
         mTabSupplier = tabSupplier;
         mNonDraggableAreasSupplier = nonDraggableAreasSupplier;
         mHeaderButtonHeight = headerButtonHeight;
+        mDisplayMode = displayMode;
 
         mScrimVisibilityObserver =
                 (isScrimVisible) -> {
@@ -153,6 +159,12 @@
                 Math.max(mCurrentHeaderState.getAppHeaderHeight(), getDefaultMinHeight()));
         mModel.set(
                 WebAppHeaderLayoutProperties.IS_VISIBLE, mCurrentHeaderState.isInDesktopWindow());
+
+        if (mIsFirstAppHeaderStateUpdate && mCurrentHeaderState.isInDesktopWindow()) {
+            RecordHistogram.recordEnumeratedHistogram(
+                    "CustomTabs.WebAppHeader.DisplayMode", mDisplayMode, DisplayMode.MAX_VALUE);
+            mIsFirstAppHeaderStateUpdate = false;
+        }
     }
 
     /**
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediatorTest.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediatorTest.java
index 1ab7f3b1..6a9dfc9 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediatorTest.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediatorTest.java
@@ -32,6 +32,7 @@
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.HistogramWatcher;
+import org.chromium.blink.mojom.DisplayMode;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.theme.ThemeColorProvider;
@@ -97,7 +98,8 @@
                         mNonDraggableAreasSupplier,
                         mThemeColorProvider,
                         SYS_APP_HEADER_HEIGHT,
-                        HEADER_BUTTON_HEIGHT);
+                        HEADER_BUTTON_HEIGHT,
+                        DisplayMode.MINIMAL_UI);
 
         mShadowLooper.idle();
     }
@@ -176,7 +178,8 @@
                         mNonDraggableAreasSupplier,
                         mThemeColorProvider,
                         SYS_APP_HEADER_HEIGHT,
-                        HEADER_BUTTON_HEIGHT);
+                        HEADER_BUTTON_HEIGHT,
+                        DisplayMode.MINIMAL_UI);
 
         assertEquals(
                 "Header min height should match app header height",
@@ -224,7 +227,8 @@
                         mNonDraggableAreasSupplier,
                         mThemeColorProvider,
                         SYS_APP_HEADER_HEIGHT,
-                        HEADER_BUTTON_HEIGHT);
+                        HEADER_BUTTON_HEIGHT,
+                        DisplayMode.MINIMAL_UI);
         assertEquals(
                 "Header paddings should match updated system insets",
                 new Rect(0, 0, 0, 0),
@@ -275,7 +279,8 @@
                         mNonDraggableAreasSupplier,
                         mThemeColorProvider,
                         SYS_APP_HEADER_HEIGHT,
-                        HEADER_BUTTON_HEIGHT);
+                        HEADER_BUTTON_HEIGHT,
+                        DisplayMode.MINIMAL_UI);
         mShadowLooper.idle();
 
         mModel.get(WebAppHeaderLayoutProperties.WIDTH_CHANGED_CALLBACK).onResult(SCREEN_WIDTH);
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index 77061b09..c75045a 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -616,12 +616,6 @@
     return;
   }
 
-  // Retrieve the ax tree id associated with the current web contents.
-  ui::AXTreeID tree_id;
-  if (content::RenderFrameHost* rfh = web_contents_->GetFocusedFrame()) {
-    tree_id = rfh->GetAXTreeID();
-  }
-
   // In order to get the AXPlatformNode for the ax node id, we first need
   // the AXPlatformNode for the web contents.
   ui::AXPlatformNode* root_platform_node =
@@ -630,8 +624,11 @@
     return;
   }
 
+  // Retrieve the ax tree id associated with the current web contents.
   ui::AXPlatformNodeDelegate* root_platform_node_delegate =
       root_platform_node->GetDelegate();
+  ui::AXTreeID tree_id =
+      root_platform_node_delegate->GetTreeData().focused_tree_id;
 
   // Now get the target node from its tree ID and node ID.
   ui::AXPlatformNode* target_node =
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index d9dd135..611097e 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -46,9 +46,9 @@
 #include "chrome/browser/command_updater.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/devtools/devtools_window_testing.h"
+#include "chrome/browser/extensions/app_tab_helper.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/first_run/first_run.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
@@ -1170,9 +1170,10 @@
 
   std::unique_ptr<WebContents> app_contents =
       WebContents::Create(WebContents::CreateParams(browser()->profile()));
-  extensions::TabHelper::CreateForWebContents(app_contents.get());
-  extensions::TabHelper* extensions_tab_helper =
-      extensions::TabHelper::FromWebContents(app_contents.get());
+  extensions::AppTabHelper::CreateForWebContents(app_contents.get());
+  extensions::AppTabHelper* extensions_tab_helper =
+      extensions::AppTabHelper::FromWebContents(app_contents.get());
+  ASSERT_TRUE(extensions_tab_helper);
   extensions_tab_helper->SetExtensionApp(extension_app);
 
   model->AddWebContents(std::move(app_contents), 0,
@@ -1469,8 +1470,8 @@
 
   // Apps launched in a window from the NTP have an extensions tab helper with
   // extension_app set.
-  ASSERT_TRUE(extensions::TabHelper::FromWebContents(app_window));
-  EXPECT_TRUE(extensions::TabHelper::FromWebContents(app_window)->is_app());
+  ASSERT_TRUE(extensions::AppTabHelper::FromWebContents(app_window));
+  EXPECT_TRUE(extensions::AppTabHelper::FromWebContents(app_window)->is_app());
   EXPECT_EQ(extensions::AppLaunchInfo::GetFullLaunchURL(extension_app),
             app_window->GetURL());
 
diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc
index 681662f..b879fda 100644
--- a/chrome/browser/ui/extensions/application_launch.cc
+++ b/chrome/browser/ui/extensions/application_launch.cc
@@ -22,9 +22,9 @@
 #include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/platform_apps/platform_app_launch.h"
+#include "chrome/browser/extensions/app_tab_helper.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/file_handlers/file_handling_launch_utils.h"
-#include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -492,9 +492,10 @@
   WebContents* const web_contents = nav_params.navigated_or_inserted_contents;
 
   // Before MV3, an extension reaching this point must have been an app. MV3
-  // added support for Web File Handlers, which don't use extension TabHelper.
+  // added support for Web File Handlers, which don't use extension
+  // AppTabHelper.
   if (extension && extension->is_app()) {
-    extensions::TabHelper::FromWebContents(web_contents)
+    extensions::AppTabHelper::FromWebContents(web_contents)
         ->SetExtensionApp(extension);
   }
 
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
index 31c6126..3c80819 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -7,8 +7,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/extensions/app_tab_helper.h"
 #include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -92,8 +92,8 @@
     return GetFallbackAppIcon();
   }
 
-  extensions::TabHelper* extensions_tab_helper =
-      extensions::TabHelper::FromWebContents(contents);
+  extensions::AppTabHelper* extensions_tab_helper =
+      extensions::AppTabHelper::FromWebContents(contents);
   if (!extensions_tab_helper) {
     return GetFallbackAppIcon();
   }
@@ -216,13 +216,14 @@
   AppBrowserController::OnTabInserted(contents);
 
   const Extension* extension = GetExtension();
-  extensions::TabHelper::FromWebContents(contents)->SetExtensionApp(extension);
+  extensions::AppTabHelper::FromWebContents(contents)->SetExtensionApp(
+      extension);
 }
 
 void HostedAppBrowserController::OnTabRemoved(content::WebContents* contents) {
   AppBrowserController::OnTabRemoved(contents);
 
-  extensions::TabHelper::FromWebContents(contents)->SetExtensionApp(nullptr);
+  extensions::AppTabHelper::FromWebContents(contents)->SetExtensionApp(nullptr);
 }
 
 void HostedAppBrowserController::LoadAppIcon(
diff --git a/chrome/browser/ui/omnibox/omnibox_search_aggregator_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_search_aggregator_browsertest.cc
index 04403f1..359e051 100644
--- a/chrome/browser/ui/omnibox/omnibox_search_aggregator_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_search_aggregator_browsertest.cc
@@ -311,7 +311,7 @@
                       GURL("https://example.com/image.png"))),
           AllOf(Field(&AutocompleteMatch::type,
                       AutocompleteMatchType::NAVSUGGEST),
-                Field(&AutocompleteMatch::contents, u""),
+                Field(&AutocompleteMatch::contents, u"Jira Issue"),
                 Field(&AutocompleteMatch::description, u"John's Document"),
                 Field(&AutocompleteMatch::destination_url,
                       GURL("https://www.example.com/"))),
diff --git a/chrome/browser/ui/tabs/alert/BUILD.gn b/chrome/browser/ui/tabs/alert/BUILD.gn
index 47ae9fc..862705e0 100644
--- a/chrome/browser/ui/tabs/alert/BUILD.gn
+++ b/chrome/browser/ui/tabs/alert/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2021 The Chromium Authors
+# Copyright 2025 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -7,5 +7,34 @@
 import("//chrome/common/features.gni")
 
 source_set("tab_alert") {
-  public = [ "tab_alert.h" ]
+  public = [
+    "tab_alert.h",
+    "tab_alert_controller.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//content/public/browser",
+  ]
+}
+
+source_set("impl") {
+  sources = [ "tab_alert_controller.cc" ]
+
+  deps = [ ":tab_alert" ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [ "tab_alert_controller_unittest.cc" ]
+
+  deps = [
+    ":impl",
+    ":tab_alert",
+    "//chrome/test:test_support",
+    "//content/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
 }
diff --git a/chrome/browser/ui/tabs/alert/tab_alert.h b/chrome/browser/ui/tabs/alert/tab_alert.h
index 0ba9dc9..0367537e 100644
--- a/chrome/browser/ui/tabs/alert/tab_alert.h
+++ b/chrome/browser/ui/tabs/alert/tab_alert.h
@@ -7,6 +7,7 @@
 
 namespace tabs {
 // Alert states for a tab. Any number of these (or none) may apply at once.
+// LINT.IfChange(TabAlert)
 enum class TabAlert {
   MEDIA_RECORDING,        // Audio/Video [both] being recorded, consumed by tab.
   TAB_CAPTURING,          // Tab contents being captured.
@@ -24,6 +25,9 @@
   VIDEO_RECORDING,           // Video [only] being recorded, consumed by tab.
   GLIC_ACCESSING,            // Glic is accessing the tab's contents.
 };
+// Any changes to the TabAlert enum needs to be updated in CompareAlerts as
+// well.
+// LINT.ThenChange(/chrome/browser/ui/tabs/alert/tab_alert_controller.cc)
 }  // namespace tabs
 
 #endif  // CHROME_BROWSER_UI_TABS_ALERT_TAB_ALERT_H_
diff --git a/chrome/browser/ui/tabs/alert/tab_alert_controller.cc b/chrome/browser/ui/tabs/alert/tab_alert_controller.cc
new file mode 100644
index 0000000..deb67372
--- /dev/null
+++ b/chrome/browser/ui/tabs/alert/tab_alert_controller.cc
@@ -0,0 +1,119 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/tabs/alert/tab_alert_controller.h"
+
+#include <functional>
+#include <optional>
+#include <vector>
+
+#include "base/check.h"
+#include "base/containers/fixed_flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/containers/to_vector.h"
+#include "chrome/browser/ui/tabs/alert/tab_alert.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_capability_type.h"
+
+namespace tabs {
+
+bool CompareAlerts::operator()(TabAlert first, TabAlert second) const {
+  // Alerts are ordered from highest priority to be shown to lowest priority.
+  static constexpr auto tab_alert_priority =
+      base::MakeFixedFlatMap<TabAlert, int>(
+          {{TabAlert::DESKTOP_CAPTURING, 14},
+           {TabAlert::TAB_CAPTURING, 13},
+           {TabAlert::MEDIA_RECORDING, 12},
+           {TabAlert::AUDIO_RECORDING, 11},
+           {TabAlert::VIDEO_RECORDING, 10},
+           {TabAlert::BLUETOOTH_CONNECTED, 9},
+           {TabAlert::BLUETOOTH_SCAN_ACTIVE, 8},
+           {TabAlert::USB_CONNECTED, 7},
+           {TabAlert::HID_CONNECTED, 6},
+           {TabAlert::SERIAL_CONNECTED, 5},
+           {TabAlert::GLIC_ACCESSING, 4},
+           {TabAlert::VR_PRESENTING_IN_HEADSET, 3},
+           {TabAlert::PIP_PLAYING, 2},
+           {TabAlert::AUDIO_MUTING, 1},
+           {TabAlert::AUDIO_PLAYING, 0}});
+
+  return tab_alert_priority.at(first) > tab_alert_priority.at(second);
+}
+
+TabAlertController::TabAlertController(content::WebContents* web_contents)
+    : content::WebContentsObserver(web_contents) {}
+
+TabAlertController::~TabAlertController() = default;
+
+base::CallbackListSubscription
+TabAlertController::AddAlertToShowChangedCallback(
+    AlertToShowChangedCallback callback) {
+  return alert_to_show_changed_callbacks_.Add(std::move(callback));
+}
+
+std::optional<TabAlert> TabAlertController::GetAlertToShow() const {
+  if (active_alerts_.empty()) {
+    return std::nullopt;
+  }
+
+  return *active_alerts_.begin();
+}
+
+std::vector<TabAlert> TabAlertController::GetAllActiveAlerts() {
+  return base::ToVector(active_alerts_);
+}
+
+void TabAlertController::OnCapabilityTypesChanged(
+    content::WebContentsCapabilityType capability_type,
+    bool used) {
+  static constexpr base::fixed_flat_map<content::WebContentsCapabilityType,
+                                        TabAlert, 5>
+      capability_type_to_alert =
+          base::MakeFixedFlatMap<content::WebContentsCapabilityType, TabAlert>(
+              {{content::WebContentsCapabilityType::kBluetoothConnected,
+                TabAlert::BLUETOOTH_CONNECTED},
+               {content::WebContentsCapabilityType::kBluetoothScanning,
+                TabAlert::BLUETOOTH_SCAN_ACTIVE},
+               {content::WebContentsCapabilityType::kUSB,
+                TabAlert::USB_CONNECTED},
+               {content::WebContentsCapabilityType::kHID,
+                TabAlert::HID_CONNECTED},
+               {content::WebContentsCapabilityType::kSerial,
+                TabAlert::SERIAL_CONNECTED}});
+
+  if (!capability_type_to_alert.contains(capability_type)) {
+    return;
+  }
+
+  const TabAlert alert = capability_type_to_alert.at(capability_type);
+  UpdateAlertState(alert, used);
+}
+
+void TabAlertController::MediaPictureInPictureChanged(
+    bool is_picture_in_picture) {
+  UpdateAlertState(TabAlert::PIP_PLAYING, is_picture_in_picture);
+}
+
+void TabAlertController::DidUpdateAudioMutingState(bool muted) {
+  UpdateAlertState(TabAlert::AUDIO_MUTING, muted);
+}
+
+void TabAlertController::OnAudioStateChanged(bool audible) {
+  UpdateAlertState(TabAlert::AUDIO_PLAYING, audible);
+}
+
+void TabAlertController::UpdateAlertState(TabAlert alert, bool is_active) {
+  std::optional<TabAlert> previous_alert = GetAlertToShow();
+  if (is_active) {
+    active_alerts_.insert(alert);
+  } else {
+    active_alerts_.erase(alert);
+  }
+
+  std::optional<TabAlert> updated_alert = GetAlertToShow();
+  if (previous_alert != updated_alert) {
+    alert_to_show_changed_callbacks_.Notify(updated_alert);
+  }
+}
+}  // namespace tabs
diff --git a/chrome/browser/ui/tabs/alert/tab_alert_controller.h b/chrome/browser/ui/tabs/alert/tab_alert_controller.h
new file mode 100644
index 0000000..b57d7946
--- /dev/null
+++ b/chrome/browser/ui/tabs/alert/tab_alert_controller.h
@@ -0,0 +1,72 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_TABS_ALERT_TAB_ALERT_CONTROLLER_H_
+#define CHROME_BROWSER_UI_TABS_ALERT_TAB_ALERT_CONTROLLER_H_
+
+#include <optional>
+#include <vector>
+
+#include "base/callback_list.h"
+#include "base/containers/flat_set.h"
+#include "chrome/browser/ui/tabs/alert/tab_alert.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace content {
+enum class WebContentsCapabilityType;
+class WebContents;
+}  // namespace content
+
+namespace tabs {
+// Comparator used to determine which tab alert has a higher priority to be
+// shown.
+struct CompareAlerts {
+  bool operator()(TabAlert first, TabAlert second) const;
+};
+
+// Observes the corresponding web contents for the tab to keep track of all
+// active alerts. Callers can subscribe and be notified when the tab alert that
+// should be shown changes.
+class TabAlertController : public content::WebContentsObserver {
+ public:
+  explicit TabAlertController(content::WebContents* web_contents);
+  TabAlertController(const TabAlertController&) = delete;
+  TabAlertController& operator=(const TabAlertController&) = delete;
+  ~TabAlertController() override;
+
+  using AlertToShowChangedCallback =
+      base::RepeatingCallback<void(std::optional<TabAlert>)>;
+  base::CallbackListSubscription AddAlertToShowChangedCallback(
+      AlertToShowChangedCallback callback);
+
+  std::optional<TabAlert> GetAlertToShow() const;
+  // Gets all active tab alerts that is sorted from highest priority
+  // to lowest priority to be shown.
+  std::vector<TabAlert> GetAllActiveAlerts();
+
+  // WebContentsObserver override:
+  void OnCapabilityTypesChanged(
+      content::WebContentsCapabilityType capability_type,
+      bool used) override;
+  void MediaPictureInPictureChanged(bool is_picture_in_picture) override;
+  void DidUpdateAudioMutingState(bool muted) override;
+  void OnAudioStateChanged(bool audible) override;
+
+ private:
+  // Adds `alert` to the set of already active alerts for this tab if it isn't
+  // currently active. Otherwise, removes `alert` from the set and is considered
+  // inactive.
+  void UpdateAlertState(TabAlert alert, bool is_active);
+
+  using AlertToShowChangedCallbackList =
+      base::RepeatingCallbackList<void(std::optional<TabAlert>)>;
+  AlertToShowChangedCallbackList alert_to_show_changed_callbacks_;
+
+  // Maintains a sorted collection of all active tab alerts from highest
+  // priority to lowest priority to be shown.
+  base::flat_set<TabAlert, CompareAlerts> active_alerts_;
+};
+}  // namespace tabs
+
+#endif  // CHROME_BROWSER_UI_TABS_ALERT_TAB_ALERT_CONTROLLER_H_
diff --git a/chrome/browser/ui/tabs/alert/tab_alert_controller_unittest.cc b/chrome/browser/ui/tabs/alert/tab_alert_controller_unittest.cc
new file mode 100644
index 0000000..5e07e018
--- /dev/null
+++ b/chrome/browser/ui/tabs/alert/tab_alert_controller_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/tabs/alert/tab_alert_controller.h"
+
+#include <memory>
+#include <optional>
+
+#include "base/functional/bind.h"
+#include "chrome/browser/ui/tabs/alert/tab_alert.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tabs {
+
+class MockTabAlertControllerSubscriber {
+ public:
+  MockTabAlertControllerSubscriber() = default;
+
+  MOCK_METHOD1(OnPrioritizedAlertStateChanged,
+               void(std::optional<TabAlert> new_alert));
+};
+
+class TabAlertControllerTest : public testing::Test {
+ public:
+  TabAlertControllerTest() {
+    web_contents_ =
+        content::WebContentsTester::CreateTestWebContents(&profile_, nullptr);
+    tab_alert_controller_ =
+        std::make_unique<TabAlertController>(web_contents_.get());
+  }
+
+  TabAlertController* tab_alert_controller() {
+    return tab_alert_controller_.get();
+  }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  content::RenderViewHostTestEnabler test_enabler_;
+  TestingProfile profile_;
+  std::unique_ptr<TabAlertController> tab_alert_controller_;
+  std::unique_ptr<content::WebContents> web_contents_;
+};
+
+TEST_F(TabAlertControllerTest, NotifiedOnAlertShouldShowChanged) {
+  auto mock_subscriber = std::make_unique<MockTabAlertControllerSubscriber>();
+  auto subscription =
+      tab_alert_controller()->AddAlertToShowChangedCallback(base::BindRepeating(
+          &MockTabAlertControllerSubscriber::OnPrioritizedAlertStateChanged,
+          base::Unretained(mock_subscriber.get())));
+
+  // Activating an alert should notify observers since it will be the only
+  // tab alert active.
+  EXPECT_CALL(*mock_subscriber,
+              OnPrioritizedAlertStateChanged(
+                  std::make_optional(TabAlert::AUDIO_PLAYING)));
+  tab_alert_controller()->OnAudioStateChanged(true);
+  ::testing::Mock::VerifyAndClearExpectations(mock_subscriber.get());
+
+  // Simulate a higher priority alert being activated.
+  EXPECT_CALL(*mock_subscriber, OnPrioritizedAlertStateChanged(
+                                    std::make_optional(TabAlert::PIP_PLAYING)));
+  tab_alert_controller()->MediaPictureInPictureChanged(true);
+  ::testing::Mock::VerifyAndClearExpectations(mock_subscriber.get());
+  EXPECT_EQ(tab_alert_controller()->GetAlertToShow(), TabAlert::PIP_PLAYING);
+
+  // Removing a lower priority tab alert shouldn't notify observers since the
+  // prioritized alert wouldn't change.
+  EXPECT_CALL(*mock_subscriber,
+              OnPrioritizedAlertStateChanged(std::optional<TabAlert>()))
+      .Times(0);
+  tab_alert_controller()->OnAudioStateChanged(false);
+  ::testing::Mock::VerifyAndClearExpectations(mock_subscriber.get());
+
+  // Remove the last active tab alert.
+  EXPECT_CALL(*mock_subscriber, OnPrioritizedAlertStateChanged(testing::_));
+  tab_alert_controller()->MediaPictureInPictureChanged(false);
+  testing::Mock::VerifyAndClearExpectations(mock_subscriber.get());
+  EXPECT_EQ(tab_alert_controller()->GetAlertToShow(), std::nullopt);
+}
+
+TEST_F(TabAlertControllerTest, GetAllAlert) {
+  tab_alert_controller()->OnAudioStateChanged(true);
+  tab_alert_controller()->OnCapabilityTypesChanged(
+      content::WebContentsCapabilityType::kBluetoothConnected, true);
+  tab_alert_controller()->MediaPictureInPictureChanged(true);
+  tab_alert_controller()->DidUpdateAudioMutingState(true);
+
+  std::optional<TabAlert> prioritized_alert =
+      tab_alert_controller()->GetAlertToShow();
+  ASSERT_TRUE(prioritized_alert.has_value());
+  EXPECT_EQ(prioritized_alert.value(), TabAlert::BLUETOOTH_CONNECTED);
+  EXPECT_EQ(tab_alert_controller()->GetAllActiveAlerts().size(), 4U);
+
+  // Verify that the active alerts list is in sorted order
+  std::vector<TabAlert> active_alerts =
+      tab_alert_controller()->GetAllActiveAlerts();
+  EXPECT_EQ(active_alerts[0], TabAlert::BLUETOOTH_CONNECTED);
+  EXPECT_EQ(active_alerts[1], TabAlert::PIP_PLAYING);
+  EXPECT_EQ(active_alerts[2], TabAlert::AUDIO_MUTING);
+  EXPECT_EQ(active_alerts[3], TabAlert::AUDIO_PLAYING);
+}
+}  // namespace tabs
diff --git a/chrome/browser/ui/tabs/public/tab_features.h b/chrome/browser/ui/tabs/public/tab_features.h
index 2bc5f30f..25ab24b 100644
--- a/chrome/browser/ui/tabs/public/tab_features.h
+++ b/chrome/browser/ui/tabs/public/tab_features.h
@@ -107,6 +107,7 @@
 
 namespace tabs {
 
+class TabAlertController;
 class TabInterface;
 class TabDialogManager;
 
@@ -254,6 +255,10 @@
   }
 #endif
 
+  TabAlertController* tab_alert_controller() {
+    return tab_alert_controller_.get();
+  }
+
   // Called exactly once to initialize features.
   // Can be overridden in tests to initialize nothing.
   virtual void Init(TabInterface& tab, Profile* profile);
@@ -392,6 +397,8 @@
 
   std::unique_ptr<MemorySaverChipTabHelper> memory_saver_chip_helper_;
 
+  std::unique_ptr<TabAlertController> tab_alert_controller_;
+
   // Must be the last member.
   base::WeakPtrFactory<TabFeatures> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/tabs/tab_features.cc b/chrome/browser/ui/tabs/tab_features.cc
index 96785f03..971fcaba 100644
--- a/chrome/browser/ui/tabs/tab_features.cc
+++ b/chrome/browser/ui/tabs/tab_features.cc
@@ -40,6 +40,7 @@
 #include "chrome/browser/ui/performance_controls/memory_saver_chip_controller.h"
 #include "chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.h"
 #include "chrome/browser/ui/performance_controls/tab_resource_usage_tab_helper.h"
+#include "chrome/browser/ui/tabs/alert/tab_alert_controller.h"
 #include "chrome/browser/ui/tabs/inactive_window_mouse_event_controller.h"
 #include "chrome/browser/ui/tabs/public/tab_dialog_manager.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_tab_data.h"
@@ -337,6 +338,9 @@
 
   memory_saver_chip_helper_ = std::make_unique<MemorySaverChipTabHelper>(tab);
 
+  tab_alert_controller_ =
+      std::make_unique<TabAlertController>(tab.GetContents());
+
   task_manager::WebContentsTags::CreateForTabContents(tab.GetContents());
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index b9a78aba..9e1dd19 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -1005,6 +1005,42 @@
   MoveGroupToImpl(group, to_index);
 }
 
+void TabStripModel::MoveSplitTo(
+    const split_tabs::SplitTabId& split_id,
+    int to_index,
+    bool pinned,
+    std::optional<tab_groups::TabGroupId> group_id) {
+  ReentrancyCheck reentrancy_check(&reentrancy_guard_);
+
+  CHECK_NE(to_index, kNoTab);
+
+  if (!group_model_ && group_id.has_value()) {
+    return;
+  }
+
+  to_index = ConstrainMoveIndex(to_index, pinned);
+
+  std::vector<std::pair<tabs::TabInterface*, int>> tabs_with_indices =
+      GetTabsAndIndicesInSplit(split_id);
+
+  // Invalid to move an to an index that breaks another split.
+  std::optional<split_tabs::SplitTabId> destination_split =
+      MoveBreaksSplitContiguity(tabs_with_indices[0].second,
+                                tabs_with_indices.size(), to_index);
+  CHECK(!destination_split.has_value());
+
+  std::vector<int> tab_indices;
+  for (const auto& tab_pair : tabs_with_indices) {
+    tab_indices.push_back(tab_pair.second);
+  }
+
+  MoveTabsWithNotifications(
+      tab_indices, to_index,
+      base::BindOnce(&tabs::TabStripCollection::MoveTabsRecursive,
+                     base::Unretained(contents_data_.get()), tab_indices,
+                     to_index, group_id, pinned));
+}
+
 void TabStripModel::MoveGroupToImpl(const tab_groups::TabGroupId& group,
                                     int to_index) {
   const gfx::Range tabs_in_group = group_model_->GetTabGroup(group)->ListTabs();
@@ -4288,9 +4324,9 @@
 
   MoveTabsWithNotifications(
       tab_indices, destination_index,
-      base::BindOnce(&tabs::TabStripCollection::MoveSplit,
-                     base::Unretained(contents_data_.get()),
-                     split->GetSplitTabId(), pinned));
+      base::BindOnce(&tabs::TabStripCollection::MoveTabsRecursive,
+                     base::Unretained(contents_data_.get()), tab_indices,
+                     destination_index, std::nullopt, pinned));
 }
 
 void TabStripModel::MoveTabsWithNotifications(
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h
index 80c73ae..1f1be058 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.h
+++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -397,10 +397,18 @@
   void MoveSelectedTabsTo(int index,
                           std::optional<tab_groups::TabGroupId> group);
 
-  // Moves all tabs in |group| to |to_index|. This has no checks to make sure
+  // Moves all tabs in `group` to `to_index`. This has no checks to make sure
   // the position is valid for a group to move to.
   void MoveGroupTo(const tab_groups::TabGroupId& group, int to_index);
 
+  // Moves all tabs in split with `split_id` to `to_index` with  properties
+  // `pinned` and `group_id`. This has no checks to make sure the position is
+  // valid for a split to move to.
+  void MoveSplitTo(const split_tabs::SplitTabId& split_id,
+                   int to_index,
+                   bool pinned,
+                   std::optional<tab_groups::TabGroupId> group_id);
+
   // Returns the currently active WebContents, or NULL if there is none.
   content::WebContents* GetActiveWebContents() const;
 
diff --git a/chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.cc b/chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.cc
index 36b29556..b6fa197 100644
--- a/chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.cc
+++ b/chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.cc
@@ -57,6 +57,19 @@
       base::Seconds(1));
 }
 
+void TabSearchToolbarButtonController::UpdateForWebUITabStrip() {
+  PinnedToolbarActionsContainer* pinned_toolbar_actions_container =
+      browser_view_->toolbar()->pinned_toolbar_actions_container();
+  if (pinned_toolbar_actions_container) {
+    actions::ActionItem* tab_search_action =
+        pinned_toolbar_actions_container->GetActionItemFor(kActionTabSearch);
+    if (tab_search_action) {
+      // Do not make tab search button available if webui tab strip is enabled.
+      tab_search_action->SetVisible(!browser_view_->webui_tab_strip());
+    }
+  }
+}
+
 void TabSearchToolbarButtonController::MaybeHideActionEphemerallyInToolbar() {
   PinnedToolbarActionsContainer* pinned_toolbar_actions_container =
       browser_view_->toolbar()->pinned_toolbar_actions_container();
diff --git a/chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.h b/chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.h
index da21631..9f364030 100644
--- a/chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.h
+++ b/chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.h
@@ -25,6 +25,8 @@
   void OnBubbleInitializing() override;
   void OnBubbleDestroying() override;
 
+  void UpdateForWebUITabStrip();
+
  private:
   void MaybeHideActionEphemerallyInToolbar();
 
diff --git a/chrome/browser/ui/views/global_media_controls/cast_device_selector_view.cc b/chrome/browser/ui/views/global_media_controls/cast_device_selector_view.cc
index 71d363ed..b7dc845 100644
--- a/chrome/browser/ui/views/global_media_controls/cast_device_selector_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/cast_device_selector_view.cc
@@ -43,6 +43,7 @@
 constexpr int kDeviceEntrySeparator = 8;
 constexpr int kCloseButtonIconSize = 20;
 constexpr int kDeviceEntryIconSize = 20;
+constexpr int kPermissionRejectedLabelWidth = 400;
 
 constexpr gfx::Insets kBackgroundInsets = gfx::Insets::TLBR(12, 8, 16, 8);
 constexpr gfx::Insets kCastHeaderRowInsets = gfx::Insets::TLBR(0, 8, 0, 4);
@@ -257,6 +258,10 @@
   permission_rejected_label_->SetDefaultEnabledColorId(
       media_color_theme_.secondary_foreground_color_id);
   permission_rejected_label_->SetText(label_text);
+  gfx::Size preferred_size(kPermissionRejectedLabelWidth,
+                           permission_rejected_label_->GetHeightForWidth(
+                               kPermissionRejectedLabelWidth));
+  permission_rejected_label_->SetPreferredSize(preferred_size);
 
 #if BUILDFLAG(IS_MAC)
   base::RepeatingClosure open_settings_cb = base::BindRepeating([]() {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
index a54902d..8a5ace7 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
@@ -147,6 +147,14 @@
 
 }  // namespace
 
+// Produces the largest centered square gfx::Rect that fits within a rectangle
+// from origin to `size`.
+gfx::Rect FullCenteredSquare(const gfx::Size& size) {
+  int side = std::min(size.width(), size.height());
+  return gfx::Rect((size.width() - side) / 2, (size.height() - side) / 2, side,
+                   side);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // OmniboxMatchCellView:
 
@@ -408,14 +416,28 @@
   // Weather icon square background should be the same color as the pop-up
   // background. The experimental thumbnail is treated similar to the
   // weather icon and is likewise resized to reduce downscaling artifacts.
-  if (is_weather_answer ||
-      (match.HasTakeoverAction(OmniboxActionId::CONTEXTUAL_SEARCH_OPEN_LENS) &&
-       omnibox_feature_configs::ContextualSearch::Get()
-           .open_lens_action_uses_thumbnail)) {
-    // Explicitly resize the weather icon to avoid pixelation.
-    gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
-        image, skia::ImageOperations::RESIZE_GOOD,
-        gfx::Size(kWeatherImageSize, kWeatherImageSize));
+  // However, thumbnails are usually rectangular and should preserve aspect
+  // ratio by cropping edges and scaling to fill, not by shrinking to fit.
+  const bool is_thumbnail =
+      match.HasTakeoverAction(OmniboxActionId::CONTEXTUAL_SEARCH_OPEN_LENS) &&
+      omnibox_feature_configs::ContextualSearch::Get()
+          .open_lens_action_uses_thumbnail;
+  if (is_weather_answer || is_thumbnail) {
+    // Explicitly resize to avoid pixelation. Note the thumbnail uses best
+    // quality because it makes a noticeable difference for so much downscaling.
+    // UX also suggested a 5% opacity black overlay to reduce white on white
+    // edgeless thumbnail effect. Reducing HSL lightness does this efficiently.
+    gfx::ImageSkia resized_image =
+        is_thumbnail ? gfx::ImageSkiaOperations::CreateHSLShiftedImage(
+                           gfx::ImageSkiaOperations::CreateResizedImage(
+                               gfx::ImageSkiaOperations::ExtractSubset(
+                                   image, FullCenteredSquare(image.size())),
+                               skia::ImageOperations::RESIZE_BEST,
+                               gfx::Size(kWeatherImageSize, kWeatherImageSize)),
+                           {-1, -1, 0.5 - (0.05 / 2)})
+                     : gfx::ImageSkiaOperations::CreateResizedImage(
+                           image, skia::ImageOperations::RESIZE_GOOD,
+                           gfx::Size(kWeatherImageSize, kWeatherImageSize));
     answer_image_view_->SetImage(ui::ImageModel::FromImageSkia(
         gfx::ImageSkiaOperations::CreateImageWithRoundRectBackground(
             gfx::SizeF(kWeatherBackgroundSize, kWeatherBackgroundSize),
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc
index b5662df..7e8f3c21 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc
@@ -133,3 +133,11 @@
   EXPECT_EQ(description_max_width, 0);
   EXPECT_EQ(iph_link_max_width, 0);
 }
+
+TEST(OmniboxMatchCellViewTest, FullCenteredSquare) {
+  extern gfx::Rect FullCenteredSquare(const gfx::Size& size);
+  EXPECT_EQ(FullCenteredSquare({0, 0}), gfx::Rect(0, 0, 0, 0));
+  EXPECT_EQ(FullCenteredSquare({28, 28}), gfx::Rect(0, 0, 28, 28));
+  EXPECT_EQ(FullCenteredSquare({50, 20}), gfx::Rect(15, 0, 20, 20));
+  EXPECT_EQ(FullCenteredSquare({20, 50}), gfx::Rect(0, 15, 20, 20));
+}
diff --git a/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc b/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc
index e92b86a..f76e415 100644
--- a/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc
@@ -16,6 +16,7 @@
 #include "components/content_settings/core/common/cookie_controls_state.h"
 #include "components/content_settings/core/common/features.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
+#include "components/privacy_sandbox/tracking_protection_settings.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/strings/grit/privacy_sandbox_strings.h"
 #include "components/vector_icons/vector_icons.h"
@@ -41,6 +42,7 @@
 namespace {
 
 using ::content_settings::CookieControlsUtil;
+using ::privacy_sandbox::IsTrackingProtectionsUi;
 
 const ui::ImageModel GetThirdPartyCookiesIcon(
     bool third_party_cookies_enabled) {
@@ -49,13 +51,6 @@
                                   : views::kEyeCrossedRefreshIcon);
 }
 
-// TODO(crbug.com/388294499): Move this logic into the privacy_sandbox/
-// directory.
-bool IsActUi(CookieControlsState controls_state) {
-  return controls_state == CookieControlsState::kActiveTp ||
-         controls_state == CookieControlsState::kPausedTp;
-}
-
 class ThirdPartyCookieLabelWrapper : public views::BoxLayoutView {
   METADATA_HEADER(ThirdPartyCookieLabelWrapper, views::BoxLayoutView)
 
@@ -246,7 +241,7 @@
 
 void PageInfoCookiesContentView::SetCookieInfo(
     const CookiesNewInfo& cookie_info) {
-  if (IsActUi(cookie_info.controls_state)) {
+  if (IsTrackingProtectionsUi(cookie_info.controls_state)) {
     SetIncognitoTrackingProtectionsDescription(cookie_info.enforcement,
                                                cookie_info.controls_state);
   } else {
@@ -263,7 +258,7 @@
             ChromeLayoutProvider::Get()->GetDistanceMetric(
                 DISTANCE_HORIZONTAL_SEPARATOR_PADDING_PAGE_INFO_VIEW)));
   }
-  if (IsActUi(cookie_info.controls_state)) {
+  if (IsTrackingProtectionsUi(cookie_info.controls_state)) {
     InitIncognitoTrackingProtectionSettingsButton();
   }
   InitCookiesDialogButton();
@@ -444,7 +439,7 @@
   tracking_protection_button_->SetID(
       PageInfoViewFactory::VIEW_ID_PAGE_INFO_ACT_PROTECTIONS_BUTTON);
 
-  if (IsActUi(controls_state)) {
+  if (IsTrackingProtectionsUi(controls_state)) {
     third_party_cookies_row_->SetVisible(false);
     tracking_protection_button_->SetVisible(true);
     third_party_cookies_container_->SetCrossAxisAlignment(
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc
index 5b3edae..a3007fe 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc
@@ -278,5 +278,10 @@
   return web_view_->GetWebContents();
 }
 
+void PrivacySandboxDialogView::SetPrivacySandboxNotice(
+    privacy_sandbox::notice::mojom::PrivacySandboxNotice notice) {
+  notice_ = notice;
+}
+
 BEGIN_METADATA(PrivacySandboxDialogView)
 END_METADATA
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.h b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.h
index 904151e..ce878a45 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.h
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.h
@@ -45,6 +45,8 @@
   void ShowNativeView() override;
   privacy_sandbox::notice::mojom::PrivacySandboxNotice GetPrivacySandboxNotice()
       override;
+  void SetPrivacySandboxNotice(
+      privacy_sandbox::notice::mojom::PrivacySandboxNotice notice) override;
 
  private:
   friend class PrivacySandboxQueueTestNotice;
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc
index dcd621a..1ee4b70 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc
@@ -176,20 +176,25 @@
 class PrivacySandboxPSNoticeDialogViewBrowserTest : public DialogBrowserTest {
  public:
   PrivacySandboxPSNoticeDialogViewBrowserTest() {
-    set_baseline("crrev.com/c/6540376");
+    set_baseline("crrev.com/c/6546899");
     scoped_feature_list_.InitWithFeatures(
         // Enabled Features
         {privacy_sandbox::kPrivacySandboxNoticeFramework},
         /*disabled_features=*/{});
   }
 
-  void SetUpOnMainThread() override {
-    mock_notice_service_ = static_cast<MockPrivacySandboxNoticeService*>(
-        PrivacySandboxNoticeServiceFactory::GetInstance()
-            ->SetTestingFactoryAndUse(
-                browser()->profile(),
-                base::BindRepeating(
-                    &privacy_sandbox::BuildMockPrivacySandboxNoticeService)));
+  void SetUpInProcessBrowserTestFixture() override {
+    create_services_subscription_ =
+        BrowserContextDependencyManager::GetInstance()
+            ->RegisterCreateServicesCallbackForTesting(
+                base::BindRepeating([](content::BrowserContext* context) {
+                  PrivacySandboxNoticeServiceFactory::GetInstance()
+                      ->SetTestingFactory(
+                          context,
+                          base::BindRepeating(
+                              &privacy_sandbox::
+                                  BuildMockPrivacySandboxNoticeService));
+                }));
   }
 
   // DialogBrowserTest:
@@ -218,14 +223,10 @@
         FROM_HERE, base::BindOnce(&PrivacySandboxDialogView::CloseNativeView,
                                   base::Unretained(dialog_view_)));
     dialog_view_ = nullptr;
-    mock_notice_service_ = nullptr;
-  }
-
-  MockPrivacySandboxNoticeService* mock_notice_service() {
-    return mock_notice_service_;
   }
 
  private:
+  base::CallbackListSubscription create_services_subscription_;
   raw_ptr<MockPrivacySandboxNoticeService> mock_notice_service_;
   raw_ptr<PrivacySandboxDialogView> dialog_view_;
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -325,7 +326,6 @@
       .WillOnce([&closed_waiter] { closed_waiter.Signal(); });
   ShowAndVerifyUi();
 
-  LOG(INFO) << "Waiting for callback";
   shown_waiter.TimedWait(kMaxWaitTime);
   closed_waiter.TimedWait(kMaxWaitTime);
 }
@@ -347,7 +347,6 @@
       .WillOnce([&closed_waiter] { closed_waiter.Signal(); });
   ShowAndVerifyUi();
 
-  LOG(INFO) << "Waiting for callback";
   shown_waiter.TimedWait(kMaxWaitTime);
   closed_waiter.TimedWait(kMaxWaitTime);
 }
@@ -370,7 +369,6 @@
       .WillOnce([&closed_waiter] { closed_waiter.Signal(); });
   ShowAndVerifyUi();
 
-  LOG(INFO) << "Waiting for callback";
   shown_waiter.TimedWait(kMaxWaitTime);
   closed_waiter.TimedWait(kMaxWaitTime);
 }
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_interactive_ui_test.cc b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_interactive_ui_test.cc
index b7b4640..efd41f0 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_interactive_ui_test.cc
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_interactive_ui_test.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/privacy_sandbox/mock_privacy_sandbox_service.h"
+#include "chrome/browser/privacy_sandbox/notice/notice_service_factory.h"
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_service.h"
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -23,6 +24,84 @@
 #include "ui/views/widget/any_widget_observer.h"
 #include "ui/views/widget/widget.h"
 
+using privacy_sandbox::notice::mojom::PrivacySandboxNotice;
+using privacy_sandbox::notice::mojom::PrivacySandboxNoticeEvent;
+
+//-----------------------------------------------------------------------------
+// Notice Framework Dialog View Interactive UI Tests.
+//-----------------------------------------------------------------------------
+class PrivacySandboxNoticeFrameworkDialogViewInteractiveUiTest
+    : public InProcessBrowserTest {
+ public:
+  PrivacySandboxNoticeFrameworkDialogViewInteractiveUiTest() {
+    feature_list_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/{{privacy_sandbox::kPrivacySandboxNoticeFramework,
+                               {}}},
+        {});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// TODO(crbug.com/408016824): Enable once GetRequiredList is implemented.
+IN_PROC_BROWSER_TEST_F(PrivacySandboxNoticeFrameworkDialogViewInteractiveUiTest,
+                       DISABLED_MultipleDialogsClosed) {
+  views::NamedWidgetShownWaiter waiter1(
+      views::test::AnyWidgetTestPasskey{},
+      PrivacySandboxDialogView::kViewClassName);
+  auto* view_manager =
+      PrivacySandboxNoticeServiceFactory::GetForProfile(browser()->profile())
+          ->GetDesktopViewManager();
+  view_manager->HandleChromeOwnedPageNavigation(browser());
+
+  auto* dialog1 = waiter1.WaitIfNeededAndGet();
+  auto* view1 = static_cast<PrivacySandboxDialogView*>(
+      dialog1->widget_delegate()->GetContentsView());
+  EXPECT_THAT(view1->GetPrivacySandboxNotice(),
+              PrivacySandboxNotice::kTopicsConsentNotice);
+
+  views::NamedWidgetShownWaiter waiter2(
+      views::test::AnyWidgetTestPasskey{},
+      PrivacySandboxDialogView::kViewClassName);
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL(chrome::kChromeUINewTabPageURL),
+      WindowOpenDisposition::NEW_WINDOW,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  view_manager->HandleChromeOwnedPageNavigation(browser());
+  auto* dialog2 = waiter2.WaitIfNeededAndGet();
+  auto* view2 = static_cast<PrivacySandboxDialogView*>(
+      dialog2->widget_delegate()->GetContentsView());
+  EXPECT_THAT(view2->GetPrivacySandboxNotice(),
+              PrivacySandboxNotice::kTopicsConsentNotice);
+
+  // Check two distinct dialogs were opened.
+  EXPECT_FALSE(dialog1->IsClosed());
+  EXPECT_FALSE(dialog2->IsClosed());
+  EXPECT_NE(dialog1, dialog2);
+
+  // Completing a consent step shouldn't close the dialogs, but all open notices
+  // should advance to the next notice.
+  view_manager->OnEventOccurred(PrivacySandboxNotice::kTopicsConsentNotice,
+                                PrivacySandboxNoticeEvent::kOptIn);
+  EXPECT_FALSE(dialog1->IsClosed());
+  EXPECT_FALSE(dialog2->IsClosed());
+  EXPECT_THAT(view1->GetPrivacySandboxNotice(),
+              PrivacySandboxNotice::kProtectedAudienceMeasurementNotice);
+  EXPECT_THAT(view2->GetPrivacySandboxNotice(),
+              PrivacySandboxNotice::kProtectedAudienceMeasurementNotice);
+
+  // When completing the notice step, close all dialogs.
+  view_manager->OnEventOccurred(
+      PrivacySandboxNotice::kProtectedAudienceMeasurementNotice,
+      PrivacySandboxNoticeEvent::kAck);
+  EXPECT_TRUE(dialog1->IsClosed());
+  EXPECT_TRUE(dialog2->IsClosed());
+}
+
+//-----------------------------------------------------------------------------
+// Pre-migration Privacy Sandbox Dialog View Interactive UI Tests.
+//-----------------------------------------------------------------------------
 class PrivacySandboxDialogViewInteractiveUiTestM1
     : public InProcessBrowserTest {
  public:
diff --git a/chrome/browser/ui/views/side_panel/side_panel_enums.h b/chrome/browser/ui/views/side_panel/side_panel_enums.h
index 8167fb2..0b287e2 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_enums.h
+++ b/chrome/browser/ui/views/side_panel/side_panel_enums.h
@@ -31,7 +31,8 @@
   kExtension = 18,
   kNewTabPage = 19,
   kReadingListToast = 20,
-  kMaxValue = kReadingListToast,
+  kNewTabFooter = 21,
+  kMaxValue = kNewTabFooter,
 };
 
 enum class SidePanelContentState {
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc
index cc71040..cc4b90a 100644
--- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc
@@ -1699,14 +1699,11 @@
     }
   }
 
-  // Revert each tab and group. N.B. we manually increment `i` because each
-  // group has multiple entries in `tab_drag_data_`.
+  // Revert each tab, split and group. We manually increment `i` because
+  // each group or split has multiple entries in `tab_drag_data_`.
   for (size_t i = 0; i < drag_data_.tab_drag_data_.size();) {
     const TabDragData tab_data = drag_data_.tab_drag_data_[i];
-    if (tab_data.view_type == TabSlotView::ViewType::kTab) {
-      RevertTabAt(i);
-      i++;
-    } else {
+    if (tab_data.view_type == TabSlotView::ViewType::kTabGroupHeader) {
       RevertGroupAt(i);
       // Skip all the tabs in the group too.
       i += source_context_->GetTabStripModel()
@@ -1714,6 +1711,23 @@
                ->GetTabGroup(tab_data.tab_group_data->group_id)
                ->tab_count() +
            1;
+    } else {
+      CHECK(tab_data.contents);
+
+      const tabs::TabInterface* tab =
+          tabs::TabInterface::GetFromContents(tab_data.contents);
+
+      if (tab->IsSplit()) {
+        split_tabs::SplitTabId split_id = tab->GetSplit().value();
+        RevertSplitAt(i);
+        i += source_context_->GetTabStripModel()
+                 ->GetSplitData(split_id)
+                 ->ListTabs()
+                 .size();
+      } else {
+        RevertTabAt(i);
+        i++;
+      }
     }
   }
 
@@ -1859,6 +1873,74 @@
       first_tab_in_group.tab_group_data->group_id, target_index);
 }
 
+void TabDragController::RevertSplitAt(size_t drag_index) {
+  CHECK_NE(current_state_, DragState::kNotStarted);
+  CHECK(attached_context_);
+  CHECK(source_context_);
+  // We can't revert if `contents` was destroyed during the drag, or if this is
+  // a group header.
+  CHECK(drag_data_.tab_drag_data_[drag_index].contents);
+
+  const TabDragData tab_data = drag_data_.tab_drag_data_[drag_index];
+  const tabs::TabInterface* tab =
+      tabs::TabInterface::GetFromContents(tab_data.contents);
+  split_tabs::SplitTabId split_id = tab->GetSplit().value();
+
+  // The split can be reverted into its original group if it still exists.
+  const std::optional<tab_groups::TabGroupId> existing_group =
+      tab_data.tab_group_data.has_value() &&
+              source_context_->GetTabStripModel()
+                  ->group_model()
+                  ->ContainsTabGroup(tab_data.tab_group_data->group_id)
+          ? std::make_optional(tab_data.tab_group_data->group_id)
+          : std::nullopt;
+
+  const int from_index =
+      attached_context_->GetTabStripModel()->GetIndexOfWebContents(
+          tab_data.contents);
+  CHECK_NE(from_index, TabStripModel::kNoTab);
+  int target_index = tab_data.source_model_index.value();
+
+  if (attached_context_ != source_context_) {
+    // The Split was inserted into another TabDragContext. We need to
+    // put it back into the original one.
+    std::unique_ptr<DetachedTabCollection> detached_split =
+        attached_context_->GetTabStripModel()->DetachSplitTabForInsertion(
+            split_id);
+    source_context_->GetTabStripModel()->InsertDetachedSplitTabAt(
+        std::move(detached_split), target_index, tab_data.pinned,
+        existing_group);
+  } else {
+    if (target_index > from_index) {
+      for (size_t i = drag_index + 1; i < drag_data_.tab_drag_data_.size();
+           ++i) {
+        const TabDragData other_tab = drag_data_.tab_drag_data_[i];
+
+        // Ignore group headers, they don't have model indices to skip over.
+        if (other_tab.view_type != TabSlotView::ViewType::kTab) {
+          continue;
+        }
+
+        tabs::TabInterface* other_tab_interface =
+            tabs::TabInterface::GetFromContents(other_tab.contents);
+
+        CHECK(other_tab_interface);
+
+        // Ignore other tabs in this split, they will get moved along with us
+        // so we won't skip over them.
+        if (other_tab_interface->GetSplit() == split_id) {
+          continue;
+        }
+
+        ++target_index;
+      }
+    }
+
+    source_context_->GetTabStripModel()->MoveSplitTo(
+        split_id, target_index, tab_data.pinned, existing_group);
+  }
+}
+
 void TabDragController::RevertTabAt(size_t drag_index) {
   CHECK_NE(current_state_, DragState::kNotStarted);
   CHECK(attached_context_);
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.h b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.h
index 45e5f05c..2764a327 100644
--- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.h
@@ -23,6 +23,7 @@
 #include "chrome/browser/ui/views/tabs/tab_strip_types.h"
 #include "components/saved_tab_groups/public/tab_group_sync_service.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "components/tabs/public/split_tab_data.h"
 #include "components/webapps/common/web_app_id.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
 #include "ui/base/models/list_selection_model.h"
@@ -413,6 +414,9 @@
   // Reverts the tab at `drag_index` in `drag_data_`.
   void RevertTabAt(size_t drag_index);
 
+  // Reverts the split starting at `drag_index_`.
+  void RevertSplitAt(size_t drag_index);
+
   // Finishes a successful drag operation.
   void CompleteDrag();
 
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
index 4e2a4bd..ff4ce68 100644
--- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
@@ -18,6 +18,7 @@
 #include <optional>
 #include <set>
 #include <utility>
+#include <vector>
 
 #include "base/command_line.h"
 #include "base/dcheck_is_on.h"
@@ -72,6 +73,7 @@
 #include "components/tabs/public/split_tab_data.h"
 #include "components/tabs/public/split_tab_id.h"
 #include "components/tabs/public/tab_group.h"
+#include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
@@ -1332,6 +1334,45 @@
   EXPECT_EQ(group_model->GetTabGroup(group1)->ListTabs(), gfx::Range(3, 4));
 }
 
+// TODO(crbug.com/333085989): Re-enable flaky tests
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_RevertDragSplitTab DISABLED_RevertDragSplitTab
+#else
+#define MAYBE_RevertDragSplitTab RevertDragSplitTab
+#endif
+
+// Drag a split tab within a tabstrip and cancel the drag.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
+                       MAYBE_RevertDragSplitTab) {
+  ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups());
+
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+  TabStripModel* model = browser()->tab_strip_model();
+  AddTabsAndResetBrowser(browser(), 3);
+  model->ActivateTabAt(0);
+  split_tabs::SplitTabId split_id =
+      model->AddToNewSplit({1}, split_tabs::SplitTabLayout::kHorizontal);
+  StopAnimating(tab_strip);
+
+  ASSERT_TRUE(PressInputAtCenter(tab_strip->tab_at(0)));
+  ASSERT_TRUE(DragInputToCenter(tab_strip->tab_at(3)));
+
+  EXPECT_EQ("2 3 0 1", IDString(model));
+  EXPECT_EQ(model->GetSplitData(split_id)->ListTabs(),
+            std::vector<tabs::TabInterface*>(
+                {model->GetTabAtIndex(2), model->GetTabAtIndex(3)}));
+
+  ASSERT_TRUE(TabDragController::IsActive());
+
+  // Pressing escape will revert the tabs to original state before the drag.
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE, false,
+                                              false, false, false));
+  EXPECT_EQ("0 1 2 3", IDString(model));
+  EXPECT_EQ(model->GetSplitData(split_id)->ListTabs(),
+            std::vector<tabs::TabInterface*>(
+                {model->GetTabAtIndex(0), model->GetTabAtIndex(1)}));
+}
+
 // Creates a browser with four tabs. The first two tabs are in Tab Group 1.
 // Dragging the third tab over one to the left will result in the tab joining
 // Tab Group 1. While this drag is still in session, pressing escape will revert
@@ -2756,8 +2797,7 @@
 #define MAYBE_DragSplitTabToSeparateWindow DragSplitTabToSeparateWindow
 #endif
 
-// Creates two browsers, then drags a combination of pinned and unpinned tabs
-// from one to the other.
+// Creates two browsers, then drags a split tab from one to the other.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        MAYBE_DragSplitTabToSeparateWindow) {
   ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups());
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index ab1b0d88..efcae14 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -46,6 +46,7 @@
 #include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_model.h"
 #include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_prefs.h"
 #include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_utils.h"
+#include "chrome/browser/ui/toolbar/pinned_toolbar/tab_search_toolbar_button_controller.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h"
@@ -599,6 +600,13 @@
 
 void ToolbarView::UpdateForWebUITabStrip() {
 #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
+  TabSearchToolbarButtonController* tab_search_toolbar_button_controller =
+      browser_->browser_window_features()
+          ->tab_search_toolbar_button_controller();
+  if (tab_search_toolbar_button_controller) {
+    tab_search_toolbar_button_controller->UpdateForWebUITabStrip();
+  }
+
   if (!new_tab_button_) {
     return;
   }
diff --git a/chrome/browser/ui/webui/customize_buttons/BUILD.gn b/chrome/browser/ui/webui/customize_buttons/BUILD.gn
new file mode 100644
index 0000000..d9404c89
--- /dev/null
+++ b/chrome/browser/ui/webui/customize_buttons/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+assert(!is_android)
+
+source_set("customize_buttons") {
+  sources = [ "customize_buttons_handler.h" ]
+  public_deps = [
+    ":mojo_bindings",
+    "//chrome/browser:browser_public_dependencies",
+    "//chrome/browser/ui/views/side_panel",
+    "//chrome/common",
+  ]
+}
+source_set("impl") {
+  sources = [ "customize_buttons_handler.cc" ]
+  deps = [
+    ":customize_buttons",
+    "//chrome/browser/profiles",
+    "//chrome/browser/ui/webui:webui_util",
+    "//components/feature_engagement/public:feature_constants",
+  ]
+}
+
+mojom("mojo_bindings") {
+  sources = [ "customize_buttons.mojom" ]
+  webui_module_path = "/"
+
+  public_deps = [ "//url/mojom:url_mojom_gurl" ]
+}
diff --git a/chrome/browser/ui/webui/customize_buttons/DIR_METADATA b/chrome/browser/ui/webui/customize_buttons/DIR_METADATA
new file mode 100644
index 0000000..684c1ac
--- /dev/null
+++ b/chrome/browser/ui/webui/customize_buttons/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//chrome/browser/resources/new_tab_shared/COMMON_METADATA"
diff --git a/chrome/browser/ui/webui/customize_buttons/OWNERS b/chrome/browser/ui/webui/customize_buttons/OWNERS
new file mode 100644
index 0000000..0fe8a7c
--- /dev/null
+++ b/chrome/browser/ui/webui/customize_buttons/OWNERS
@@ -0,0 +1,7 @@
+pauladedeji@google.com
+romanarora@chromium.org
+rtatum@google.com
+tiborg@chromium.org
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom b/chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom
new file mode 100644
index 0000000..da42e5a
--- /dev/null
+++ b/chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom
@@ -0,0 +1,51 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module customize_buttons.mojom;
+
+// TODO(crbug.com/403572608) Dedupe CustomizeChromeSection mojom enums.
+enum CustomizeChromeSection {
+  kUnspecified,
+  kAppearance,
+  kShortcuts,
+  kModules,
+  kWallpaperSearch,
+  kToolbar,
+};
+
+enum SidePanelOpenTrigger {
+  kNewTabPage,
+  kNewTabFooter,
+};
+
+// Used by the WebUI document to bootstrap bidirectional communication.
+interface CustomizeButtonsHandlerFactory {
+  // The WebUI page's |BrowserProxy| singleton calls this method when the
+  // document is first initialized.
+  CreateCustomizeButtonsHandler(
+      pending_remote<CustomizeButtonsDocument> page,
+      pending_receiver<CustomizeButtonsHandler> handler);
+};
+
+// Browser-side handler for requests from WebUI document.
+interface CustomizeButtonsHandler {
+  // Increment the number of times the user has opened the side panel
+  // with the customize chrome button.
+  IncrementCustomizeChromeButtonOpenCount();
+
+  // Increment the number of times the user has seen the wallpaper search
+  // button.
+  IncrementWallpaperSearchButtonShownCount();
+
+  // Shows or hides the customize chrome in unified side panel.
+  SetCustomizeChromeSidePanelVisible(bool visible,
+                                     CustomizeChromeSection section,
+                                     SidePanelOpenTrigger trigger);
+};
+
+// WebUI-side handler for requests from the browser.
+interface CustomizeButtonsDocument {
+  // Sets Customize Chrome Side Panel visibility to |visible|.
+  SetCustomizeChromeSidePanelVisibility(bool visible);
+};
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.cc b/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.cc
new file mode 100644
index 0000000..141c91c
--- /dev/null
+++ b/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.cc
@@ -0,0 +1,159 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.h"
+
+#include <utility>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/tabs/public/tab_features.h"
+#include "chrome/browser/ui/views/side_panel/side_panel_enums.h"
+#include "chrome/browser/ui/webui/webui_embedding_context.h"
+#include "chrome/common/pref_names.h"
+#include "components/feature_engagement/public/feature_constants.h"
+#include "components/prefs/pref_service.h"
+#include "components/tabs/public/tab_interface.h"
+
+CustomizeButtonsHandler::CustomizeButtonsHandler(
+    mojo::PendingReceiver<customize_buttons::mojom::CustomizeButtonsHandler>
+        pending_handler,
+    mojo::PendingRemote<customize_buttons::mojom::CustomizeButtonsDocument>
+        pending_page,
+    Profile* profile,
+    content::WebContents* web_contents,
+    std::unique_ptr<NewTabPageFeaturePromoHelper>
+        customize_chrome_feature_promo_helper)
+    : profile_(profile),
+      web_contents_(web_contents),
+      feature_promo_helper_(std::move(customize_chrome_feature_promo_helper)),
+      tab_changed_subscription_(webui::RegisterTabInterfaceChanged(
+          web_contents,
+          base::BindRepeating(&CustomizeButtonsHandler::OnTabInterfaceChanged,
+                              base::Unretained(this)))),
+      page_{std::move(pending_page)},
+      receiver_{this, std::move(pending_handler)} {
+  CHECK(web_contents_);
+  CHECK(feature_promo_helper_);
+  OnTabInterfaceChanged();
+}
+
+CustomizeButtonsHandler::~CustomizeButtonsHandler() = default;
+
+void CustomizeButtonsHandler::OnTabInterfaceChanged() {
+  tabs::TabInterface* tab_interface =
+      webui::GetTabInterface(web_contents_.get());
+  if (!tab_interface) {
+    // TODO(crbug.com/378475391): NTP or Footer should always load into a
+    // WebContents owned by a TabModel. Remove this once NTP loading has been
+    // restricted to browser tabs only.
+    LOG(ERROR)
+        << "NewTabPage or NewTabFooter loaded into a non-browser-tab context";
+
+    // Reset any composed tab features here.
+    SetCustomizeChromeSidePanelController(nullptr);
+    return;
+  }
+
+  SetCustomizeChromeSidePanelController(
+      tab_interface->GetTabFeatures()
+          ->customize_chrome_side_panel_controller());
+}
+
+void CustomizeButtonsHandler::SetCustomizeChromeSidePanelController(
+    customize_chrome::SidePanelController* side_panel_controller) {
+  customize_chrome_side_panel_controller_ = side_panel_controller;
+
+  if (customize_chrome_side_panel_controller_) {
+    page_->SetCustomizeChromeSidePanelVisibility(
+        customize_chrome_side_panel_controller_
+            ->IsCustomizeChromeEntryShowing());
+    customize_chrome_side_panel_controller_->SetEntryChangedCallback(
+        base::BindRepeating(&CustomizeButtonsHandler::
+                                NotifyCustomizeChromeSidePanelVisibilityChanged,
+                            weak_ptr_factory_.GetWeakPtr()));
+  } else {
+    page_->SetCustomizeChromeSidePanelVisibility(false);
+  }
+}
+
+void CustomizeButtonsHandler::NotifyCustomizeChromeSidePanelVisibilityChanged(
+    bool is_open) {
+  page_->SetCustomizeChromeSidePanelVisibility(is_open);
+}
+
+void CustomizeButtonsHandler::SetCustomizeChromeSidePanelVisible(
+    bool visible,
+    customize_buttons::mojom::CustomizeChromeSection section,
+    customize_buttons::mojom::SidePanelOpenTrigger trigger) {
+  CHECK(customize_chrome_side_panel_controller_);
+  if (!visible) {
+    customize_chrome_side_panel_controller_->CloseSidePanel();
+    return;
+  }
+
+  CustomizeChromeSection section_enum;
+  // TODO(crbug.com/419081665) Dedupe CustomizeChromeSection mojom enums.
+  switch (section) {
+    case customize_buttons::mojom::CustomizeChromeSection::kUnspecified:
+      section_enum = CustomizeChromeSection::kUnspecified;
+      break;
+    case customize_buttons::mojom::CustomizeChromeSection::kAppearance:
+      section_enum = CustomizeChromeSection::kAppearance;
+      break;
+    case customize_buttons::mojom::CustomizeChromeSection::kShortcuts:
+      section_enum = CustomizeChromeSection::kShortcuts;
+      break;
+    case customize_buttons::mojom::CustomizeChromeSection::kModules:
+      section_enum = CustomizeChromeSection::kModules;
+      break;
+    case customize_buttons::mojom::CustomizeChromeSection::kWallpaperSearch:
+      section_enum = CustomizeChromeSection::kWallpaperSearch;
+      break;
+    case customize_buttons::mojom::CustomizeChromeSection::kToolbar:
+      section_enum = CustomizeChromeSection::kToolbar;
+      break;
+  }
+
+  SidePanelOpenTrigger trigger_enum;
+  switch (trigger) {
+    case customize_buttons::mojom::SidePanelOpenTrigger::kNewTabPage:
+      trigger_enum = SidePanelOpenTrigger::kNewTabPage;
+      break;
+    case customize_buttons::mojom::SidePanelOpenTrigger::kNewTabFooter:
+      trigger_enum = SidePanelOpenTrigger::kNewTabFooter;
+      break;
+  }
+
+  customize_chrome_side_panel_controller_->OpenSidePanel(trigger_enum,
+                                                         section_enum);
+
+  // Record usage for customize chrome promo.
+  auto* tab = web_contents_.get();
+  feature_promo_helper_->RecordPromoFeatureUsageAndClosePromo(
+      feature_engagement::kIPHDesktopCustomizeChromeRefreshFeature, tab);
+  feature_promo_helper_->RecordPromoFeatureUsageAndClosePromo(
+      feature_engagement::kIPHDesktopCustomizeChromeFeature, tab);
+}
+
+void CustomizeButtonsHandler::IncrementCustomizeChromeButtonOpenCount() {
+  CHECK(profile_);
+  CHECK(profile_->GetPrefs());
+  profile_->GetPrefs()->SetInteger(
+      prefs::kNtpCustomizeChromeButtonOpenCount,
+      profile_->GetPrefs()->GetInteger(
+          prefs::kNtpCustomizeChromeButtonOpenCount) +
+          1);
+}
+
+void CustomizeButtonsHandler::IncrementWallpaperSearchButtonShownCount() {
+  const auto shown_count = profile_->GetPrefs()->GetInteger(
+      prefs::kNtpWallpaperSearchButtonShownCount);
+  profile_->GetPrefs()->SetInteger(prefs::kNtpWallpaperSearchButtonShownCount,
+                                   shown_count + 1);
+}
+
+void CustomizeButtonsHandler::SetCustomizeChromeSidePanelControllerForTesting(
+    customize_chrome::SidePanelController* side_panel_controller) {
+  SetCustomizeChromeSidePanelController(side_panel_controller);
+}
diff --git a/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.h b/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.h
new file mode 100644
index 0000000..325324e
--- /dev/null
+++ b/chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.h
@@ -0,0 +1,73 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CUSTOMIZE_BUTTONS_CUSTOMIZE_BUTTONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CUSTOMIZE_BUTTONS_CUSTOMIZE_BUTTONS_HANDLER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/new_tab_page/feature_promo_helper/new_tab_page_feature_promo_helper.h"
+#include "chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h"
+#include "chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+class Profile;
+
+class CustomizeButtonsHandler
+    : public customize_buttons::mojom::CustomizeButtonsHandler {
+ public:
+  CustomizeButtonsHandler(
+      mojo::PendingReceiver<customize_buttons::mojom::CustomizeButtonsHandler>
+          pending_handler,
+      mojo::PendingRemote<customize_buttons::mojom::CustomizeButtonsDocument>
+          pending_page,
+      Profile* profile,
+      content::WebContents* web_contents,
+      std::unique_ptr<NewTabPageFeaturePromoHelper>
+          customize_chrome_feature_promo_helper);
+  CustomizeButtonsHandler(const CustomizeButtonsHandler&) = delete;
+  CustomizeButtonsHandler& operator=(const CustomizeButtonsHandler&) = delete;
+  ~CustomizeButtonsHandler() override;
+
+  // Called when the embedding TabInterface has changed.
+  // TODO(crbug.com/378475391): This can be removed once the NTP has been
+  // restricted from loading in app windows.
+  void OnTabInterfaceChanged();
+
+  void NotifyCustomizeChromeSidePanelVisibilityChanged(bool is_open);
+
+  // customize_buttons::mojom::CustomizeButtonsHandler:
+  void IncrementCustomizeChromeButtonOpenCount() override;
+  void IncrementWallpaperSearchButtonShownCount() override;
+  void SetCustomizeChromeSidePanelVisible(
+      bool visible,
+      customize_buttons::mojom::CustomizeChromeSection section,
+      customize_buttons::mojom::SidePanelOpenTrigger triger) override;
+
+  void SetCustomizeChromeSidePanelControllerForTesting(
+      customize_chrome::SidePanelController* side_panel_controller);
+
+ private:
+  void SetCustomizeChromeSidePanelController(
+      customize_chrome::SidePanelController* side_panel_controller);
+
+  raw_ptr<Profile> profile_;
+  raw_ptr<content::WebContents> web_contents_;
+  std::unique_ptr<NewTabPageFeaturePromoHelper> feature_promo_helper_;
+
+  // TODO(crbug.com/378475391): Make this const once the TabModel is guaranteed
+  // to be present during load and fixed for the NTP's lifetime.
+  raw_ptr<customize_chrome::SidePanelController>
+      customize_chrome_side_panel_controller_;
+  base::CallbackListSubscription tab_changed_subscription_;
+
+  mojo::Remote<customize_buttons::mojom::CustomizeButtonsDocument> page_;
+  mojo::Receiver<customize_buttons::mojom::CustomizeButtonsHandler> receiver_;
+
+  base::WeakPtrFactory<CustomizeButtonsHandler> weak_ptr_factory_{this};
+};
+#endif  // CHROME_BROWSER_UI_WEBUI_CUSTOMIZE_BUTTONS_CUSTOMIZE_BUTTONS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
index cd9968b..e563193 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
@@ -165,15 +165,6 @@
   kLinkCopy,
 };
 
-enum CustomizeChromeSection {
-  kUnspecified,
-  kAppearance,
-  kShortcuts,
-  kModules,
-  kWallpaperSearch,
-  kToolbar,
-};
-
 struct PromoImagePart {
   url.mojom.Url image_url;
   url.mojom.Url target;
@@ -300,17 +291,8 @@
   GetModulesOrder() => (array<string> module_ids);
   // Triggers a call to |SetModulesLoadable|.
   UpdateModulesLoadable();
-  // Shows or hides the customize chrome in unified side panel.
-  SetCustomizeChromeSidePanelVisible(bool visible,
-                                     CustomizeChromeSection section);
-  // Increment the number of times the user has opened the side panel
-  // with the customize chrome button.
-  IncrementCustomizeChromeButtonOpenCount();
   // Attempts to trigger the specified in product help promo.
   MaybeShowFeaturePromo(IphFeature iph_feature);
-  // Increment the number of times the user has seen the wallpaper search
-  // button.
-  IncrementWallpaperSearchButtonShownCount();
   // Returns a base64 webp encoded string representing the image for the QR code
   // on the mobile promo.
   GetMobilePromoQrCode() => (string qrCode);
@@ -357,8 +339,6 @@
 
 // WebUI-side handler for requests from the browser.
 interface Page {
-  // Sets Customize Chrome Side Panel visibility to |visible|.
-  SetCustomizeChromeSidePanelVisibility(bool visible);
   // Sets the current theme.
   SetTheme(Theme theme);
   // Disables the modules in |ids|. If |all|, disables all modules and passes an
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
index 9cbc486ed..9df2a7a 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -55,12 +55,8 @@
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/hats/hats_service.h"
 #include "chrome/browser/ui/hats/hats_service_factory.h"
-#include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h"
-#include "chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h"
 #include "chrome/browser/ui/webui/new_tab_page/ntp_pref_names.h"
-#include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
-#include "chrome/browser/ui/webui/webui_embedding_context.h"
 #include "chrome/browser/ui/webui/webui_util_desktop.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
@@ -503,10 +499,6 @@
       promo_service_(PromoServiceFactory::GetForProfile(profile)),
       interaction_module_id_trigger_dict_(
           MakeModuleInteractionTriggerIdDictionary()),
-      tab_changed_subscription_(webui::RegisterTabInterfaceChanged(
-          web_contents,
-          base::BindRepeating(&NewTabPageHandler::OnTabInterfaceChanged,
-                              base::Unretained(this)))),
       page_{std::move(pending_page)},
       receiver_{this, std::move(pending_page_handler)} {
   CHECK(ntp_background_service_);
@@ -561,8 +553,6 @@
       prefs::kSeedColorChangeCount,
       base::BindRepeating(&NewTabPageHandler::MaybeShowWebstoreToast,
                           base::Unretained(this)));
-
-  OnTabInterfaceChanged();
 }
 
 NewTabPageHandler::~NewTabPageHandler() {
@@ -906,58 +896,6 @@
   }
 }
 
-void NewTabPageHandler::SetCustomizeChromeSidePanelVisible(
-    bool visible,
-    new_tab_page::mojom::CustomizeChromeSection section_mojo) {
-  CHECK(customize_chrome_side_panel_controller_);
-  if (!visible) {
-    customize_chrome_side_panel_controller_->CloseSidePanel();
-    return;
-  }
-
-  CustomizeChromeSection section_enum;
-  switch (section_mojo) {
-    case new_tab_page::mojom::CustomizeChromeSection::kUnspecified:
-      section_enum = CustomizeChromeSection::kUnspecified;
-      break;
-    case new_tab_page::mojom::CustomizeChromeSection::kAppearance:
-      section_enum = CustomizeChromeSection::kAppearance;
-      break;
-    case new_tab_page::mojom::CustomizeChromeSection::kShortcuts:
-      section_enum = CustomizeChromeSection::kShortcuts;
-      break;
-    case new_tab_page::mojom::CustomizeChromeSection::kModules:
-      section_enum = CustomizeChromeSection::kModules;
-      break;
-    case new_tab_page::mojom::CustomizeChromeSection::kWallpaperSearch:
-      section_enum = CustomizeChromeSection::kWallpaperSearch;
-      break;
-    case new_tab_page::mojom::CustomizeChromeSection::kToolbar:
-      section_enum = CustomizeChromeSection::kToolbar;
-      break;
-  }
-
-  customize_chrome_side_panel_controller_->OpenSidePanel(
-      SidePanelOpenTrigger::kNewTabPage, section_enum);
-
-  // Record usage for customize chrome promo.
-  auto* tab = web_contents_.get();
-  feature_promo_helper_->RecordPromoFeatureUsageAndClosePromo(
-      feature_engagement::kIPHDesktopCustomizeChromeRefreshFeature, tab);
-  feature_promo_helper_->RecordPromoFeatureUsageAndClosePromo(
-      feature_engagement::kIPHDesktopCustomizeChromeFeature, tab);
-}
-
-void NewTabPageHandler::IncrementCustomizeChromeButtonOpenCount() {
-  CHECK(profile_);
-  CHECK(profile_->GetPrefs());
-  profile_->GetPrefs()->SetInteger(
-      prefs::kNtpCustomizeChromeButtonOpenCount,
-      profile_->GetPrefs()->GetInteger(
-          prefs::kNtpCustomizeChromeButtonOpenCount) +
-          1);
-}
-
 void NewTabPageHandler::MaybeShowFeaturePromo(
     new_tab_page::mojom::IphFeature iph_feature) {
   CHECK(profile_);
@@ -983,13 +921,6 @@
   }
 }
 
-void NewTabPageHandler::IncrementWallpaperSearchButtonShownCount() {
-  const auto shown_count = profile_->GetPrefs()->GetInteger(
-      prefs::kNtpWallpaperSearchButtonShownCount);
-  profile_->GetPrefs()->SetInteger(prefs::kNtpWallpaperSearchButtonShownCount,
-                                   shown_count + 1);
-}
-
 void NewTabPageHandler::OnAppRendered(double time) {
   logger_.LogEvent(NTP_APP_RENDERED,
                    base::Time::FromMillisecondsSinceUnixEpoch(time) -
@@ -1148,11 +1079,6 @@
   LogEvent(NTP_MIDDLE_SLOT_PROMO_LINK_CLICKED);
 }
 
-void NewTabPageHandler::SetCustomizeChromeSidePanelControllerForTesting(
-    customize_chrome::SidePanelController* side_panel_controller) {
-  SetCustomizeChromeSidePanelController(side_panel_controller);
-}
-
 void NewTabPageHandler::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
   OnThemeChanged();
 }
@@ -1324,25 +1250,6 @@
   }
 }
 
-void NewTabPageHandler::OnTabInterfaceChanged() {
-  tabs::TabInterface* tab_interface =
-      webui::GetTabInterface(web_contents_.get());
-  if (!tab_interface) {
-    // TODO(crbug.com/378475391): NTP should always load into a WebContents
-    // owned by a TabModel. Remove this once NTP loading has been restricted to
-    // browser tabs only.
-    LOG(ERROR) << "NewTabPage loaded into a non-browser-tab context";
-
-    // Reset any composed tab features here.
-    SetCustomizeChromeSidePanelController(nullptr);
-    return;
-  }
-
-  SetCustomizeChromeSidePanelController(
-      tab_interface->GetTabFeatures()
-          ->customize_chrome_side_panel_controller());
-}
-
 void NewTabPageHandler::OnLogoAvailable(
     GetDoodleCallback callback,
     search_provider_logos::LogoCallbackReason type,
@@ -1489,11 +1396,6 @@
   return profile_->GetPrefs()->GetBoolean(ntp_prefs::kNtpShortcutsVisible);
 }
 
-void NewTabPageHandler::NotifyCustomizeChromeSidePanelVisibilityChanged(
-    bool is_open) {
-  page_->SetCustomizeChromeSidePanelVisibility(is_open);
-}
-
 void NewTabPageHandler::MaybeLaunchInteractionSurvey(
     std::string_view interaction,
     const std::string& module_id,
@@ -1681,23 +1583,6 @@
                                    false);
 }
 
-void NewTabPageHandler::SetCustomizeChromeSidePanelController(
-    customize_chrome::SidePanelController* side_panel_controller) {
-  customize_chrome_side_panel_controller_ = side_panel_controller;
-
-  if (customize_chrome_side_panel_controller_) {
-    page_->SetCustomizeChromeSidePanelVisibility(
-        customize_chrome_side_panel_controller_
-            ->IsCustomizeChromeEntryShowing());
-    customize_chrome_side_panel_controller_->SetEntryChangedCallback(
-        base::BindRepeating(
-            &NewTabPageHandler::NotifyCustomizeChromeSidePanelVisibilityChanged,
-            weak_ptr_factory_.GetWeakPtr()));
-  } else {
-    page_->SetCustomizeChromeSidePanelVisibility(false);
-  }
-}
-
 void NewTabPageHandler::SetModuleHidden(const std::string& module_id,
                                         bool hidden) {
   ScopedListPrefUpdate update(profile_->GetPrefs(), prefs::kNtpHiddenModules);
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
index ec03c4c..4a3fdac 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
@@ -154,17 +154,12 @@
   void SetModulesOrder(const std::vector<std::string>& module_ids) override;
   void GetModulesOrder(GetModulesOrderCallback callback) override;
   void UpdateModulesLoadable() override;
-  void SetCustomizeChromeSidePanelVisible(
-      bool visible,
-      new_tab_page::mojom::CustomizeChromeSection section) override;
-  void IncrementCustomizeChromeButtonOpenCount() override;
   void GetMobilePromoQrCode(GetMobilePromoQrCodeCallback callback) override;
   void OnMobilePromoShown() override;
   void OnDismissMobilePromo() override;
   void OnUndoDismissMobilePromo() override;
   void MaybeShowFeaturePromo(
       new_tab_page::mojom::IphFeature iph_feature) override;
-  void IncrementWallpaperSearchButtonShownCount() override;
   void OnAppRendered(double time) override;
   void OnOneGoogleBarRendered(double time) override;
   void OnPromoRendered(double time,
@@ -215,11 +210,6 @@
   void FileSelected(const ui::SelectedFileInfo& file, int index) override;
   void FileSelectionCanceled() override;
 
-  // Called when the embedding TabInterface has changed.
-  // TODO(crbug.com/378475391): This can be removed once the NTP has been
-  // restricted from loading in app windows.
-  void OnTabInterfaceChanged();
-
   void OnLogoAvailable(
       GetDoodleCallback callback,
       search_provider_logos::LogoCallbackReason type,
@@ -240,7 +230,6 @@
 
   bool IsCustomLinksEnabled() const;
   bool IsShortcutsVisible() const;
-  void NotifyCustomizeChromeSidePanelVisibilityChanged(bool is_open);
   void MaybeLaunchInteractionSurvey(std::string_view interaction,
                                     const std::string& module_id,
                                     int delay_time_ms = 0);
@@ -267,8 +256,6 @@
       base::Time request_start_time,
       const segmentation_platform::ClassificationResult& result);
 
-  void SetCustomizeChromeSidePanelController(
-      customize_chrome::SidePanelController* side_panel_controller);
   void SetModuleHidden(const std::string& module_id, bool hidden);
 
   // Synchronizes Microsoft module enablement with their current authentication
@@ -325,8 +312,6 @@
   raw_ptr<customize_chrome::SidePanelController>
       customize_chrome_side_panel_controller_;
 
-  base::CallbackListSubscription tab_changed_subscription_;
-
   // These are located at the end of the list of member variables to ensure the
   // WebUI page is disconnected before other members are destroyed.
   mojo::Remote<new_tab_page::mojom::Page> page_;
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
index 060fa18..8bc2c3f 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
@@ -43,6 +43,8 @@
 #include "chrome/browser/ui/hats/hats_service_factory.h"
 #include "chrome/browser/ui/hats/mock_hats_service.h"
 #include "chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h"
+#include "chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom.h"
+#include "chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.h"
 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h"
 #include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
 #include "chrome/browser/ui/webui/webui_util_desktop.h"
@@ -125,6 +127,28 @@
   mojo::Receiver<new_tab_page::mojom::Page> receiver_{this};
 };
 
+// TODO(crbug.com/394906790) Consolidate tests related to CustomizeButtonHandler
+// into customize_buttons_handler_unittest.cc for better modularity.
+class MockCustomizeButtonsDocument
+    : public customize_buttons::mojom::CustomizeButtonsDocument {
+ public:
+  MockCustomizeButtonsDocument() = default;
+  ~MockCustomizeButtonsDocument() override = default;
+
+  mojo::PendingRemote<customize_buttons::mojom::CustomizeButtonsDocument>
+  BindAndGetRemote() {
+    DCHECK(!receiver_.is_bound());
+    return receiver_.BindNewPipeAndPassRemote();
+  }
+
+  void FlushForTesting() { receiver_.FlushForTesting(); }
+
+  MOCK_METHOD(void, SetCustomizeChromeSidePanelVisibility, (bool));
+
+  mojo::Receiver<customize_buttons::mojom::CustomizeButtonsDocument> receiver_{
+      this};
+};
+
 class MockLogoService : public search_provider_logos::LogoService {
  public:
   MOCK_METHOD(void, GetLogo, (search_provider_logos::LogoCallbacks, bool));
@@ -308,6 +332,11 @@
         mock_feature_promo_helper_(new MockFeaturePromoHelper()),
         mock_feature_promo_helper_ptr_(std::unique_ptr<MockFeaturePromoHelper>(
             mock_feature_promo_helper_)),
+        mock_feature_promo_helper_for_customize_buttons_(
+            new MockFeaturePromoHelper()),
+        mock_feature_promo_helper_for_customize_buttons_ptr_(
+            std::unique_ptr<MockFeaturePromoHelper>(
+                mock_feature_promo_helper_for_customize_buttons_)),
         mock_customize_chrome_tab_helper_(
             std::make_unique<MockCustomizeChromeTabHelper>()) {
     mock_hats_service_ = static_cast<MockHatsService*>(
@@ -354,14 +383,22 @@
         &mock_segmentation_platform_service_, web_contents_,
         std::move(mock_feature_promo_helper_ptr_), base::Time::Now(),
         &module_id_details);
-    handler_->SetCustomizeChromeSidePanelControllerForTesting(
-        mock_customize_chrome_tab_helper_.get());
     mock_page_.FlushForTesting();
     EXPECT_EQ(handler_.get(), theme_service_observer_);
     EXPECT_EQ(handler_.get(), ntp_custom_background_service_observer_);
     testing::Mock::VerifyAndClearExpectations(&mock_page_);
     testing::Mock::VerifyAndClearExpectations(
         &mock_ntp_custom_background_service_);
+
+    customizeButtonsHandler_ = std::make_unique<CustomizeButtonsHandler>(
+        mojo::PendingReceiver<
+            customize_buttons::mojom::CustomizeButtonsHandler>(),
+        mock_customize_buttons_doc_.BindAndGetRemote(), profile_.get(),
+        web_contents_,
+        std::move(mock_feature_promo_helper_for_customize_buttons_ptr_));
+    customizeButtonsHandler_->SetCustomizeChromeSidePanelControllerForTesting(
+        mock_customize_chrome_tab_helper_.get());
+    mock_customize_buttons_doc_.FlushForTesting();
   }
 
   new_tab_page::mojom::DoodlePtr GetDoodle(
@@ -395,6 +432,7 @@
 
  protected:
   testing::NiceMock<MockPage> mock_page_;
+  testing::NiceMock<MockCustomizeButtonsDocument> mock_customize_buttons_doc_;
   // NOTE: The initialization order of these members matters.
   content::BrowserTaskEnvironment task_environment_;
   network::TestURLLoaderFactory test_url_loader_factory_;
@@ -415,10 +453,16 @@
   // Pointer to mock that will eventually be solely owned by the handler.
   raw_ptr<MockFeaturePromoHelper, DanglingUntriaged> mock_feature_promo_helper_;
   std::unique_ptr<MockFeaturePromoHelper> mock_feature_promo_helper_ptr_;
+  // Pointer to mock that will eventually be solely owned by the handler.
+  raw_ptr<MockFeaturePromoHelper, DisableDanglingPtrDetection>
+      mock_feature_promo_helper_for_customize_buttons_;
+  std::unique_ptr<MockFeaturePromoHelper>
+      mock_feature_promo_helper_for_customize_buttons_ptr_;
   std::unique_ptr<MockCustomizeChromeTabHelper>
       mock_customize_chrome_tab_helper_;
   base::HistogramTester histogram_tester_;
   std::unique_ptr<NewTabPageHandler> handler_;
+  std::unique_ptr<CustomizeButtonsHandler> customizeButtonsHandler_;
   raw_ptr<ThemeServiceObserver> theme_service_observer_;
   raw_ptr<NtpCustomBackgroundServiceObserver>
       ntp_custom_background_service_observer_;
@@ -1224,22 +1268,23 @@
       .WillOnce(testing::DoAll(testing::SaveArg<0>(&trigger),
                                testing::SaveArg<1>(&section)));
   EXPECT_CALL(
-      *mock_feature_promo_helper_,
+      *mock_feature_promo_helper_for_customize_buttons_,
       RecordPromoFeatureUsageAndClosePromo(
           testing::Ref(feature_engagement::kIPHDesktopCustomizeChromeFeature),
           web_contents_.get()))
       .Times(1);
   EXPECT_CALL(
-      *mock_feature_promo_helper_,
+      *mock_feature_promo_helper_for_customize_buttons_,
       RecordPromoFeatureUsageAndClosePromo(
           testing::Ref(
               feature_engagement::kIPHDesktopCustomizeChromeRefreshFeature),
           web_contents_.get()))
       .Times(1);
 
-  handler_->SetCustomizeChromeSidePanelVisible(
+  customizeButtonsHandler_->SetCustomizeChromeSidePanelVisible(
       /*visible=*/true,
-      new_tab_page::mojom::CustomizeChromeSection::kAppearance);
+      customize_buttons::mojom::CustomizeChromeSection::kAppearance,
+      customize_buttons::mojom::SidePanelOpenTrigger::kNewTabPage);
 
   EXPECT_EQ(SidePanelOpenTrigger::kNewTabPage, trigger);
   EXPECT_EQ(CustomizeChromeSection::kAppearance, section);
@@ -1247,11 +1292,14 @@
 
 TEST_F(NewTabPageHandlerTest, CloseSidePanel) {
   EXPECT_CALL(*mock_customize_chrome_tab_helper_, CloseSidePanel).Times(1);
-  EXPECT_CALL(*mock_feature_promo_helper_, RecordPromoFeatureUsageAndClosePromo)
+  EXPECT_CALL(*mock_feature_promo_helper_for_customize_buttons_,
+              RecordPromoFeatureUsageAndClosePromo)
       .Times(0);
 
-  handler_->SetCustomizeChromeSidePanelVisible(
-      /*visible=*/false, new_tab_page::mojom::CustomizeChromeSection::kModules);
+  customizeButtonsHandler_->SetCustomizeChromeSidePanelVisible(
+      /*visible=*/false,
+      customize_buttons::mojom::CustomizeChromeSection::kModules,
+      customize_buttons::mojom::SidePanelOpenTrigger::kNewTabPage);
 }
 
 TEST_F(NewTabPageHandlerTest, IncrementCustomizeChromeButtonOpenCount) {
@@ -1259,13 +1307,13 @@
                 prefs::kNtpCustomizeChromeButtonOpenCount),
             0);
 
-  handler_->IncrementCustomizeChromeButtonOpenCount();
+  customizeButtonsHandler_->IncrementCustomizeChromeButtonOpenCount();
 
   EXPECT_EQ(profile_->GetPrefs()->GetInteger(
                 prefs::kNtpCustomizeChromeButtonOpenCount),
             1);
 
-  handler_->IncrementCustomizeChromeButtonOpenCount();
+  customizeButtonsHandler_->IncrementCustomizeChromeButtonOpenCount();
 
   EXPECT_EQ(profile_->GetPrefs()->GetInteger(
                 prefs::kNtpCustomizeChromeButtonOpenCount),
@@ -1286,7 +1334,7 @@
   handler_->MaybeShowFeaturePromo(
       new_tab_page::mojom::IphFeature::kCustomizeChrome);
 
-  handler_->IncrementCustomizeChromeButtonOpenCount();
+  customizeButtonsHandler_->IncrementCustomizeChromeButtonOpenCount();
   EXPECT_EQ(profile_->GetPrefs()->GetInteger(
                 prefs::kNtpCustomizeChromeButtonOpenCount),
             1);
@@ -1350,7 +1398,7 @@
                 prefs::kNtpWallpaperSearchButtonShownCount),
             0);
 
-  handler_->IncrementWallpaperSearchButtonShownCount();
+  customizeButtonsHandler_->IncrementWallpaperSearchButtonShownCount();
 
   EXPECT_EQ(profile_->GetPrefs()->GetInteger(
                 prefs::kNtpWallpaperSearchButtonShownCount),
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 88ecce4..76e77f7 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -19,7 +19,6 @@
 #include "base/values.h"
 #include "chrome/browser/browser_features.h"
 #include "chrome/browser/buildflags.h"
-#include "chrome/browser/new_tab_page/feature_promo_helper/new_tab_page_feature_promo_helper.h"
 #include "chrome/browser/new_tab_page/modules/file_suggestion/drive_service.h"
 #include "chrome/browser/new_tab_page/modules/file_suggestion/drive_suggestion_handler.h"
 #include "chrome/browser/new_tab_page/modules/file_suggestion/microsoft_files_page_handler.h"
@@ -45,6 +44,7 @@
 #include "chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h"
 #include "chrome/browser/ui/webui/browser_command/browser_command_handler.h"
 #include "chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h"
+#include "chrome/browser/ui/webui/customize_buttons/customize_buttons_handler.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
 #include "chrome/browser/ui/webui/metrics_reporter/metrics_reporter_service.h"
 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h"
@@ -505,6 +505,7 @@
     : ui::MojoWebUIController(web_ui, /*enable_chrome_send=*/true),
       content::WebContentsObserver(web_ui->GetWebContents()),
       page_factory_receiver_(this),
+      customize_buttons_factory_receiver_(this),
       most_visited_page_factory_receiver_(this),
       browser_command_factory_receiver_(this),
       profile_(Profile::FromWebUI(web_ui)),
@@ -670,6 +671,16 @@
 }
 
 void NewTabPageUI::BindInterface(
+    mojo::PendingReceiver<
+        customize_buttons::mojom::CustomizeButtonsHandlerFactory>
+        pending_receiver) {
+  if (customize_buttons_factory_receiver_.is_bound()) {
+    customize_buttons_factory_receiver_.reset();
+  }
+  customize_buttons_factory_receiver_.Bind(std::move(pending_receiver));
+}
+
+void NewTabPageUI::BindInterface(
     mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandlerFactory>
         pending_receiver) {
   if (most_visited_page_factory_receiver_.is_bound()) {
@@ -788,6 +799,16 @@
       web_ui()->GetWebContents());
 }
 
+void NewTabPageUI::CreateCustomizeButtonsHandler(
+    mojo::PendingRemote<customize_buttons::mojom::CustomizeButtonsDocument>
+        pending_page,
+    mojo::PendingReceiver<customize_buttons::mojom::CustomizeButtonsHandler>
+        pending_page_handler) {
+  customize_buttons_handler_ = std::make_unique<CustomizeButtonsHandler>(
+      std::move(pending_page_handler), std::move(pending_page), profile_,
+      web_contents(), std::make_unique<NewTabPageFeaturePromoHelper>());
+}
+
 void NewTabPageUI::CreatePageHandler(
     mojo::PendingRemote<most_visited::mojom::MostVisitedPage> pending_page,
     mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandler>
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
index 61d9cad6..8b36443 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -29,6 +29,7 @@
 #include "chrome/browser/search/background/ntp_custom_background_service_observer.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_observer.h"
+#include "chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom.h"
 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/page_image_service/mojom/page_image_service.mojom.h"
@@ -65,6 +66,7 @@
 }  // namespace ui
 
 class BrowserCommandHandler;
+class CustomizeButtonsHandler;
 class DriveSuggestionHandler;
 #if !defined(OFFICIAL_BUILD)
 class FooHandler;
@@ -96,13 +98,15 @@
   bool IsWebUIEnabled(content::BrowserContext* browser_context) override;
 };
 
-class NewTabPageUI : public ui::MojoWebUIController,
-                     public new_tab_page::mojom::PageHandlerFactory,
-                     public most_visited::mojom::MostVisitedPageHandlerFactory,
-                     public browser_command::mojom::CommandHandlerFactory,
-                     public help_bubble::mojom::HelpBubbleHandlerFactory,
-                     public NtpCustomBackgroundServiceObserver,
-                     content::WebContentsObserver {
+class NewTabPageUI
+    : public ui::MojoWebUIController,
+      public new_tab_page::mojom::PageHandlerFactory,
+      public customize_buttons::mojom::CustomizeButtonsHandlerFactory,
+      public most_visited::mojom::MostVisitedPageHandlerFactory,
+      public browser_command::mojom::CommandHandlerFactory,
+      public help_bubble::mojom::HelpBubbleHandlerFactory,
+      public NtpCustomBackgroundServiceObserver,
+      content::WebContentsObserver {
  public:
   explicit NewTabPageUI(content::WebUI* web_ui);
 
@@ -144,6 +148,13 @@
           pending_receiver);
 
   // Instantiates the implementor of the
+  // customize_buttons::mojom::CustomizeButtonsHandlerFactory mojo interface
+  // passing the pending receiver that will be internally bound.
+  void BindInterface(mojo::PendingReceiver<
+                     customize_buttons::mojom::CustomizeButtonsHandlerFactory>
+                         pending_receiver);
+
+  // Instantiates the implementor of the
   // most_visited::mojom::MostVisitedPageHandlerFactory mojo interface passing
   // the pending receiver that will be internally bound.
   void BindInterface(
@@ -223,6 +234,13 @@
       mojo::PendingReceiver<browser_command::mojom::CommandHandler>
           pending_handler) override;
 
+  // customize_buttons::mojom::CustomizeButtonsHandlerFactory:
+  void CreateCustomizeButtonsHandler(
+      mojo::PendingRemote<customize_buttons::mojom::CustomizeButtonsDocument>
+          pending_page,
+      mojo::PendingReceiver<customize_buttons::mojom::CustomizeButtonsHandler>
+          pending_page_handler) override;
+
   // most_visited::mojom::MostVisitedPageHandlerFactory:
   void CreatePageHandler(
       mojo::PendingRemote<most_visited::mojom::MostVisitedPage> pending_page,
@@ -258,6 +276,9 @@
   mojo::Receiver<new_tab_page::mojom::PageHandlerFactory>
       page_factory_receiver_;
   std::unique_ptr<ui::ColorChangeHandler> color_provider_handler_;
+  std::unique_ptr<CustomizeButtonsHandler> customize_buttons_handler_;
+  mojo::Receiver<customize_buttons::mojom::CustomizeButtonsHandlerFactory>
+      customize_buttons_factory_receiver_;
   std::unique_ptr<MostVisitedHandler> most_visited_page_handler_;
   mojo::Receiver<most_visited::mojom::MostVisitedPageHandlerFactory>
       most_visited_page_factory_receiver_;
diff --git a/chrome/browser/ui/webui/privacy_sandbox/base_dialog.mojom b/chrome/browser/ui/webui/privacy_sandbox/base_dialog.mojom
index 1214d31..e44e3bb 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/base_dialog.mojom
+++ b/chrome/browser/ui/webui/privacy_sandbox/base_dialog.mojom
@@ -7,6 +7,15 @@
 import "mojo/public/mojom/base/values.mojom";
 import "chrome/browser/privacy_sandbox/notice/notice.mojom";
 
+// Factory ensures that the Page and PageHandler interfaces are always created
+// together without requiring an initialization call from the WebUI to the
+// handler.
+interface BaseDialogPageHandlerFactory {
+  // Crate a page handler for the base dialog and link it to the UI.
+  CreatePageHandler(pending_remote<BaseDialogPage> page,
+  pending_receiver<BaseDialogPageHandler> receiver);
+};
+
 // Renderer -> Browser
 interface BaseDialogPageHandler {
   // Resizes the native view dialog based on the height of the document.
@@ -14,9 +23,13 @@
   ResizeDialog(uint32 height);
   // Shows the Native View dialog.
   ShowDialog();
-  // Closes the Native View dialog.
-  CloseDialog();
   // Sends Event Occurred to the View Manager.
   EventOccurred(privacy_sandbox.notice.mojom.PrivacySandboxNotice notice,
     privacy_sandbox.notice.mojom.PrivacySandboxNoticeEvent event);
 };
+
+// Called from C++ side of chrome://privacy-sandbox-base-dialog. (Browser -> Renderer)
+interface BaseDialogPage {
+  // Notifies the existing notice to navigate to the next step.
+  NavigateToNextStep(privacy_sandbox.notice.mojom.PrivacySandboxNotice next_step);
+};
diff --git a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.cc b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.cc
index 6df597e..019f8c1 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.cc
+++ b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.cc
@@ -8,15 +8,18 @@
 
 namespace privacy_sandbox {
 
+using dialog::mojom::BaseDialogPage;
 using dialog::mojom::BaseDialogPageHandler;
 using notice::mojom::PrivacySandboxNotice;
 using notice::mojom::PrivacySandboxNoticeEvent;
 
 BaseDialogHandler::BaseDialogHandler(
     mojo::PendingReceiver<BaseDialogPageHandler> receiver,
+    mojo::PendingRemote<BaseDialogPage> page,
     DesktopViewManagerInterface* view_manager,
     BaseDialogUIDelegate* delegate)
     : receiver_(this, std::move(receiver)),
+      page_(std::move(page)),
       delegate_(delegate),
       view_manager_(view_manager) {
   CHECK(view_manager_);
@@ -41,13 +44,6 @@
   delegate_->ShowNativeView();
 }
 
-void BaseDialogHandler::CloseDialog() {
-  if (!delegate_) {
-    return;
-  }
-  delegate_->CloseNativeView();
-}
-
 void BaseDialogHandler::EventOccurred(PrivacySandboxNotice notice,
                                       PrivacySandboxNoticeEvent event) {
   view_manager_->OnEventOccurred(notice, event);
@@ -55,7 +51,15 @@
 
 void BaseDialogHandler::MaybeNavigateToNextStep(
     std::optional<PrivacySandboxNotice> next_id) {
-  // TODO(crbug.com/408016824): implement and add tests.
+  if (!delegate_) {
+    return;
+  }
+  if (!next_id) {
+    delegate_->CloseNativeView();
+  } else {
+    page_->NavigateToNextStep(*next_id);
+    delegate_->SetPrivacySandboxNotice(*next_id);
+  }
 }
 
 }  // namespace privacy_sandbox
diff --git a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.h b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.h
index 565a93e04..415d911 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.h
+++ b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.h
@@ -10,6 +10,7 @@
 #include "chrome/browser/privacy_sandbox/notice/notice.mojom-forward.h"
 #include "chrome/browser/ui/webui/privacy_sandbox/base_dialog.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace privacy_sandbox {
 
@@ -21,6 +22,7 @@
  public:
   BaseDialogHandler(
       mojo::PendingReceiver<dialog::mojom::BaseDialogPageHandler> receiver,
+      mojo::PendingRemote<dialog::mojom::BaseDialogPage> page,
       DesktopViewManagerInterface* view_manager,
       BaseDialogUIDelegate* delegate);
 
@@ -37,7 +39,6 @@
   // privacy_sandbox::dialog::mojom::BaseDialogPageHandler
   void ResizeDialog(uint32_t height) override;
   void ShowDialog() override;
-  void CloseDialog() override;
   void EventOccurred(notice::mojom::PrivacySandboxNotice notice,
                      notice::mojom::PrivacySandboxNoticeEvent event) override;
 
@@ -46,6 +47,7 @@
                           DesktopViewManagerInterface::Observer>
       desktop_view_manager_observation_{this};
   mojo::Receiver<dialog::mojom::BaseDialogPageHandler> receiver_;
+  mojo::Remote<dialog::mojom::BaseDialogPage> page_;
   raw_ptr<BaseDialogUIDelegate> delegate_;
   raw_ptr<DesktopViewManagerInterface> view_manager_;
   bool has_resized = false;
diff --git a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler_unittest.cc b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler_unittest.cc
index 80cb73d..dbd57800 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler_unittest.cc
+++ b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler_unittest.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/privacy_sandbox/notice/mocks/mock_desktop_view_manager.h"
 #include "chrome/browser/privacy_sandbox/notice/notice.mojom.h"
 #include "chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.h"
+#include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -31,6 +32,26 @@
   MOCK_METHOD(void, ShowNativeView, (), (override));
   MOCK_METHOD(void, CloseNativeView, (), (override));
   MOCK_METHOD(PrivacySandboxNotice, GetPrivacySandboxNotice, (), (override));
+  MOCK_METHOD(void,
+              SetPrivacySandboxNotice,
+              (PrivacySandboxNotice),
+              (override));
+};
+
+class MockBaseDialogPage : public dialog::mojom::BaseDialogPage {
+ public:
+  ~MockBaseDialogPage() override = default;
+
+  mojo::PendingRemote<dialog::mojom::BaseDialogPage> BindAndGetRemote() {
+    DCHECK(!receiver_.is_bound());
+    return receiver_.BindNewPipeAndPassRemote();
+  }
+
+  void FlushForTesting() { receiver_.FlushForTesting(); }
+
+  mojo::Receiver<dialog::mojom::BaseDialogPage> receiver_{this};
+
+  MOCK_METHOD(void, NavigateToNextStep, (notice::mojom::PrivacySandboxNotice));
 };
 
 class PrivacySandboxBaseDialogHandlerTest : public testing::Test {
@@ -38,9 +59,12 @@
   PrivacySandboxBaseDialogHandlerTest() = default;
 
  protected:
+  content::BrowserTaskEnvironment task_environment_;
+  MockBaseDialogPage mock_page_;
   MockBaseDialogUIDelegate mock_delegate_;
   MockDesktopViewManager view_manager_;
-  BaseDialogHandler handler_{mojo::NullReceiver(), &view_manager_,
+  BaseDialogHandler handler_{mojo::NullReceiver(),
+                             mock_page_.BindAndGetRemote(), &view_manager_,
                              &mock_delegate_};
 };
 
@@ -49,18 +73,6 @@
   handler_.ShowDialog();
 }
 
-TEST_F(PrivacySandboxBaseDialogHandlerTest, CloseDialog) {
-  EXPECT_CALL(mock_delegate_, CloseNativeView()).Times(1);
-  handler_.CloseDialog();
-}
-
-TEST_F(PrivacySandboxBaseDialogHandlerTest, ShowThenCloseDialog) {
-  EXPECT_CALL(mock_delegate_, ShowNativeView()).Times(1);
-  EXPECT_CALL(mock_delegate_, CloseNativeView()).Times(1);
-  handler_.ShowDialog();
-  handler_.CloseDialog();
-}
-
 TEST_F(PrivacySandboxBaseDialogHandlerTest, EventOccurred) {
   EXPECT_CALL(view_manager_, OnEventOccurred(kTestNotice, kTestEvent));
   handler_.EventOccurred(kTestNotice, kTestEvent);
@@ -76,23 +88,49 @@
   EXPECT_DEATH_IF_SUPPORTED(handler_.ResizeDialog(kTestHeight2), "");
 }
 
+class PrivacySandboxBaseDialogNavigateTest
+    : public PrivacySandboxBaseDialogHandlerTest,
+      public testing::WithParamInterface<std::optional<PrivacySandboxNotice>> {
+};
+
+TEST_P(PrivacySandboxBaseDialogNavigateTest,
+       MaybeNavigateToNextStepWithValidId) {
+  std::optional<PrivacySandboxNotice> notice = GetParam();
+  if (!notice) {
+    EXPECT_CALL(mock_delegate_, CloseNativeView()).Times(1);
+  } else {
+    EXPECT_CALL(mock_delegate_, SetPrivacySandboxNotice(notice.value()))
+        .Times(1);
+    EXPECT_CALL(mock_page_, NavigateToNextStep(notice.value())).Times(1);
+  }
+  handler_.MaybeNavigateToNextStep(notice);
+  mock_page_.FlushForTesting();
+  testing::Mock::VerifyAndClearExpectations(&mock_page_);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    PrivacySandboxBaseDialogNavigateTest,
+    PrivacySandboxBaseDialogNavigateTest,
+    testing::Values(std::nullopt,
+                    PrivacySandboxNotice::kTopicsConsentNotice,
+                    PrivacySandboxNotice::kProtectedAudienceMeasurementNotice,
+                    PrivacySandboxNotice::kThreeAdsApisNotice,
+                    PrivacySandboxNotice::kMeasurementNotice));
+
 class PrivacySandboxBaseDialogHandlerNullDelegateTest : public testing::Test {
  public:
   PrivacySandboxBaseDialogHandlerNullDelegateTest() = default;
 
  protected:
   MockDesktopViewManager view_manager_;
-  BaseDialogHandler handler_{mojo::NullReceiver(), &view_manager_, nullptr};
+  BaseDialogHandler handler_{mojo::NullReceiver(), mojo::NullRemote(),
+                             &view_manager_, nullptr};
 };
 
 TEST_F(PrivacySandboxBaseDialogHandlerNullDelegateTest, ShowDialog) {
   EXPECT_NO_FATAL_FAILURE(handler_.ShowDialog());
 }
 
-TEST_F(PrivacySandboxBaseDialogHandlerNullDelegateTest, CloseDialog) {
-  EXPECT_NO_FATAL_FAILURE(handler_.CloseDialog());
-}
-
 TEST_F(PrivacySandboxBaseDialogHandlerNullDelegateTest, ResizeDialog) {
   const int kTestHeight = 500;
   EXPECT_NO_FATAL_FAILURE(handler_.ResizeDialog(kTestHeight));
@@ -106,5 +144,11 @@
                          PrivacySandboxNoticeEvent::kOptIn);
 }
 
+TEST_F(PrivacySandboxBaseDialogHandlerNullDelegateTest,
+       MaybeNavigateToNextStep) {
+  EXPECT_NO_FATAL_FAILURE(handler_.MaybeNavigateToNextStep(
+      PrivacySandboxNotice::kTopicsConsentNotice));
+}
+
 }  // namespace
 }  // namespace privacy_sandbox
diff --git a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.cc b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.cc
index 896b650..bfe3c3a 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.cc
+++ b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.cc
@@ -18,6 +18,7 @@
 
 namespace privacy_sandbox {
 
+using dialog::mojom::BaseDialogPage;
 using dialog::mojom::BaseDialogPageHandler;
 using notice::mojom::PrivacySandboxNotice;
 
@@ -51,12 +52,21 @@
 BaseDialogUI::~BaseDialogUI() = default;
 
 void BaseDialogUI::BindInterface(
+    mojo::PendingReceiver<BaseDialogPageHandlerFactory> receiver) {
+  page_factory_receiver_.reset();
+  page_factory_receiver_.Bind(std::move(receiver));
+}
+
+void BaseDialogUI::CreatePageHandler(
+    mojo::PendingRemote<BaseDialogPage> page,
     mojo::PendingReceiver<BaseDialogPageHandler> receiver) {
+  // Checks that the PendingRemote is bound.
+  CHECK(page);
   if (auto* privacy_sandbox_notice_service =
           PrivacySandboxNoticeServiceFactory::GetForProfile(
               Profile::FromWebUI(web_ui()))) {
     page_handler_ = std::make_unique<BaseDialogHandler>(
-        std::move(receiver),
+        std::move(receiver), std::move(page),
         privacy_sandbox_notice_service->GetDesktopViewManager(), delegate_);
   }
 }
diff --git a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.h b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.h
index bcf4e705..37e9039 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.h
+++ b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.h
@@ -26,10 +26,13 @@
   virtual void ShowNativeView() = 0;
   virtual void CloseNativeView() = 0;
   virtual notice::mojom::PrivacySandboxNotice GetPrivacySandboxNotice() = 0;
+  virtual void SetPrivacySandboxNotice(
+      notice::mojom::PrivacySandboxNotice notice) = 0;
 };
 
 // MojoWebUIController for Privacy Sandbox Base Dialog
-class BaseDialogUI : public ui::MojoWebUIController {
+class BaseDialogUI : public ui::MojoWebUIController,
+                     public dialog::mojom::BaseDialogPageHandlerFactory {
  public:
   explicit BaseDialogUI(content::WebUI* web_ui);
   BaseDialogUI(const BaseDialogUI&) = delete;
@@ -41,9 +44,20 @@
   // privacy_sandbox::dialog::mojom::BaseDialogPageHandler mojo interface
   // passing the pending receiver that will be internally bound.
   void BindInterface(
-      mojo::PendingReceiver<dialog::mojom::BaseDialogPageHandler> receiver);
+      mojo::PendingReceiver<dialog::mojom::BaseDialogPageHandlerFactory>
+          receiver);
 
  private:
+  // The PendingRemote must be valid and bind to a receiver in order to start
+  // sending messages to the receiver. This is set in
+  // base_dialog_browser_proxy.ts. dialog::mojom::BaseDialogPageHandlerFactory:
+  void CreatePageHandler(
+      mojo::PendingRemote<dialog::mojom::BaseDialogPage> page,
+      mojo::PendingReceiver<dialog::mojom::BaseDialogPageHandler> receiver)
+      override;
+
+  mojo::Receiver<dialog::mojom::BaseDialogPageHandlerFactory>
+      page_factory_receiver_{this};
   std::unique_ptr<BaseDialogHandler> page_handler_;
   raw_ptr<BaseDialogUIDelegate> delegate_;
 
diff --git a/chrome/browser/vr/ui.h b/chrome/browser/vr/ui.h
index 10618a0..3b42b4c7 100644
--- a/chrome/browser/vr/ui.h
+++ b/chrome/browser/vr/ui.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_VR_UI_H_
 
 #include <memory>
-#include <queue>
 #include <utility>
 #include <vector>
 
diff --git a/chrome/browser/win/installer_downloader/BUILD.gn b/chrome/browser/win/installer_downloader/BUILD.gn
index 441d2f0..43d41a6 100644
--- a/chrome/browser/win/installer_downloader/BUILD.gn
+++ b/chrome/browser/win/installer_downloader/BUILD.gn
@@ -49,6 +49,7 @@
   sources = [ "installer_downloader_infobar_delegate.cc" ]
 
   deps = [
+    ":controller",
     "//base",
     "//chrome/app:branded_strings_grit",
     "//chrome/app:generated_resources_grit",
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_feature.h b/chrome/browser/win/installer_downloader/installer_downloader_feature.h
index efb402ab..66fcb1b 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_feature.h
+++ b/chrome/browser/win/installer_downloader/installer_downloader_feature.h
@@ -5,13 +5,20 @@
 #ifndef CHROME_BROWSER_WIN_INSTALLER_DOWNLOADER_INSTALLER_DOWNLOADER_FEATURE_H_
 #define CHROME_BROWSER_WIN_INSTALLER_DOWNLOADER_INSTALLER_DOWNLOADER_FEATURE_H_
 
+#include <string>
+
 #include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 
 namespace installer_downloader {
 
 // When enabled, user may see the installer download UI flow.
 BASE_DECLARE_FEATURE(kInstallerDownloader);
 
+// Intentionally defaulted to empty string. It will be set by experiment.
+inline constexpr base::FeatureParam<std::string>
+    kLearnMoreUrl(&kInstallerDownloader, "LearnMoreUrl", "");
+
 }  // namespace installer_downloader
 
 #endif  // CHROME_BROWSER_WIN_INSTALLER_DOWNLOADER_INSTALLER_DOWNLOADER_FEATURE_H_
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc
index 1faa8ca..987891ab 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc
+++ b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc
@@ -5,20 +5,25 @@
 #include "chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.h"
 
 #include <memory>
+#include <string>
 #include <utility>
 
+#include "base/check.h"
 #include "base/functional/callback.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/ui/views/infobars/confirm_infobar.h"
+#include "chrome/browser/win/installer_downloader/installer_downloader_feature.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/infobars/content/content_infobar_manager.h"
 #include "components/infobars/core/infobar.h"
 #include "components/infobars/core/infobar_manager.h"
 #include "components/omnibox/browser/vector_icons.h"
+#include "components/strings/grit/components_strings.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/vector_icon_types.h"
+#include "url/gurl.h"
 
 namespace installer_downloader {
 
@@ -76,7 +81,7 @@
 }
 
 std::u16string InstallerDownloaderInfoBarDelegate::GetLinkText() const {
-  return l10n_util::GetStringUTF16(IDS_INSTALLER_DOWNLOADER_LINK_TEXT);
+  return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
 }
 
 int InstallerDownloaderInfoBarDelegate::GetButtons() const {
@@ -88,11 +93,12 @@
   return l10n_util::GetStringUTF16(IDS_INSTALLER_DOWNLOADER_BUTTON_LABEL);
 }
 
-bool InstallerDownloaderInfoBarDelegate::LinkClicked(
-    WindowOpenDisposition disposition) {
-  // TODO(crbug.com/412697757): Add implementation of what will happen once link
-  // is clicked.
-  return true;
+GURL InstallerDownloaderInfoBarDelegate::GetLinkURL() const {
+  const std::string learn_more_url_str = kLearnMoreUrl.Get();
+  GURL learn_more_url(learn_more_url_str);
+  CHECK(learn_more_url.is_valid());
+
+  return learn_more_url;
 }
 
 }  // namespace installer_downloader
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.h b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.h
index ff90c3e..3dd72aa 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.h
+++ b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.h
@@ -42,7 +42,7 @@
   std::u16string GetLinkText() const override;
   int GetButtons() const override;
   std::u16string GetButtonLabel(InfoBarButton button) const override;
-  bool LinkClicked(WindowOpenDisposition disposition) override;
+  GURL GetLinkURL() const override;
 
  private:
   base::OnceClosure accept_cb_;
diff --git a/chrome/browser_exposed_mojom_targets.gni b/chrome/browser_exposed_mojom_targets.gni
index 2443127..15249a5 100644
--- a/chrome/browser_exposed_mojom_targets.gni
+++ b/chrome/browser_exposed_mojom_targets.gni
@@ -28,6 +28,7 @@
   "//chrome/browser/ui/webui/app_service_internals:mojo_bindings",
   "//chrome/browser/ui/webui/bluetooth_internals:mojo_bindings",
   "//chrome/browser/ui/webui/connectors_internals:mojo_bindings",
+  "//chrome/browser/ui/webui/customize_buttons:mojo_bindings",
   "//chrome/browser/ui/webui/discards:mojo_bindings",
   "//chrome/browser/ui/webui/downloads:mojo_bindings",
   "//chrome/browser/ui/webui/user_education_internals:mojo_bindings",
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 5946352..664229a 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1747763949-862ba35743570b1388e0908419acc48660817e39-a2a9986393624ba3e4c621acfbabca8a5ff76866.profdata
+chrome-android32-main-1747806850-8917f411974bdc3098ed5961eb19a35d229d00d7-95eb5ccc52fe0508982ed6981ee4b981aaaa40a3.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 13b8ece..8c3c9ed 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1747768602-c3a25191590271747e5a4a657470637614e1b175-2433064901720dc6739ae4bf29a4d5281397043b.profdata
+chrome-android64-main-1747803472-3a4a2cb4b363bf182930bbac7348d25811ea134c-5cec444561b50f41e8d20cf114c0aff8c2cd8960.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 272a041..f76d551c69 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1747771183-7e8480ac405ebb979c92ef1626346d542786c7e8-16edc46f3f4ef93b1e06d871bd371a9a75b292ee.profdata
+chrome-mac-arm-main-1747806850-ac3f97de4de319ed04456015ef90c7ba902f3ae0-95eb5ccc52fe0508982ed6981ee4b981aaaa40a3.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 28d5b316..95095ac 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1747763949-1ea80dd1344988e541bc7c34d8680f7fc1569f93-a2a9986393624ba3e4c621acfbabca8a5ff76866.profdata
+chrome-mac-main-1747785545-b0f00527a66ef794b965e213a7d0a678e7fafa94-a1e83f26ff53e34007b34f7f23bb9f542e16f6c1.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 728f274..58adf73 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1747763949-a4e4fee37fd4402eb194cb8df2437c3d90fca234-a2a9986393624ba3e4c621acfbabca8a5ff76866.profdata
+chrome-win-arm64-main-1747785545-f11ff070903b50e05e06f3f6c69475d5a017cc58-a1e83f26ff53e34007b34f7f23bb9f542e16f6c1.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 904dd88..678dc04 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1747742334-7f959dbfd853b8e8bf8add55915a869290dc042f-3313d9b6b84ff455dbf0edfee0bb15bbc23b30ba.profdata
+chrome-win32-main-1747785545-64012ef5bd2d9aa43b3613d4d5173b1a5c7c515a-a1e83f26ff53e34007b34f7f23bb9f542e16f6c1.profdata
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index ccdc5b3..f81bcf68 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -2821,6 +2821,9 @@
     "ssl.device_post_quantum_enabled";
 #endif
 
+// Boolean that specifies whether TLS 1.3 Early Data is enabled.
+inline constexpr char kTLS13EarlyDataEnabled[] = "ssl.tls13_early_data_enabled";
+
 // If false, disable Encrypted ClientHello (ECH) in TLS connections.
 inline constexpr char kEncryptedClientHelloEnabled[] = "ssl.ech_enabled";
 
diff --git a/chrome/installer/linux/BUILD.gn b/chrome/installer/linux/BUILD.gn
index 00478ab2..d0a2c8a 100644
--- a/chrome/installer/linux/BUILD.gn
+++ b/chrome/installer/linux/BUILD.gn
@@ -36,14 +36,10 @@
   "$root_out_dir/chrome_sandbox",
 ]
 
-packaging_files_shlibs = []
-
-if (!use_static_angle) {
-  packaging_files_shlibs += [
-    "$root_out_dir/libEGL.so",
-    "$root_out_dir/libGLESv2.so",
-  ]
-}
+packaging_files_shlibs = [
+  "$root_out_dir/libEGL.so",
+  "$root_out_dir/libGLESv2.so",
+]
 
 if (bundle_widevine_cdm) {
   packaging_files_shlibs +=
@@ -366,6 +362,8 @@
     ":strip_chrome_crashpad_handler",
     ":strip_chrome_management_service",
     ":strip_chrome_sandbox",
+    ":strip_libEGL_shlib",
+    ":strip_libGLESv2_shlib",
     ":theme_files",
     "//chrome",
     "//chrome:packed_resources",
@@ -374,18 +372,12 @@
     "//components/crash/core/app:chrome_crashpad_handler",
     "//components/privacy_sandbox/privacy_sandbox_attestations/preload:component",
     "//sandbox/linux:chrome_sandbox",
+    "//third_party/angle:libEGL",
+    "//third_party/angle:libGLESv2",
   ]
   if (bundle_widevine_cdm) {
     public_deps += [ "//third_party/widevine/cdm" ]
   }
-  if (!use_static_angle) {
-    public_deps += [
-      ":strip_libEGL_shlib",
-      ":strip_libGLESv2_shlib",
-      "//third_party/angle:libEGL",
-      "//third_party/angle:libGLESv2",
-    ]
-  }
   if (angle_shared_libvulkan && !is_chromeos) {
     public_deps += [
       ":strip_libvulkan_shlib",
diff --git a/chrome/renderer/bound_session_credentials/bound_session_request_throttled_in_renderer_manager_unittest.cc b/chrome/renderer/bound_session_credentials/bound_session_request_throttled_in_renderer_manager_unittest.cc
index 23d1e00..169f88e 100644
--- a/chrome/renderer/bound_session_credentials/bound_session_request_throttled_in_renderer_manager_unittest.cc
+++ b/chrome/renderer/bound_session_credentials/bound_session_request_throttled_in_renderer_manager_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/renderer/bound_session_credentials/bound_session_request_throttled_in_renderer_manager.h"
 
 #include <memory>
+#include <queue>
 
 #include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
diff --git a/chrome/services/sharing/nearby/platform/ble_v2_medium.cc b/chrome/services/sharing/nearby/platform/ble_v2_medium.cc
index b801283a..8d2634c3 100644
--- a/chrome/services/sharing/nearby/platform/ble_v2_medium.cc
+++ b/chrome/services/sharing/nearby/platform/ble_v2_medium.cc
@@ -507,7 +507,7 @@
 }
 
 std::unique_ptr<api::ble_v2::GattClient> BleV2Medium::ConnectToGattServer(
-    api::ble_v2::BlePeripheral& peripheral,
+    api::ble_v2::BlePeripheral::UniqueId peripheral_id,
     api::ble_v2::TxPowerLevel tx_power_level,
     api::ble_v2::ClientGattConnectionCallback callback) {
   if (!features::IsNearbyBleV2Enabled()) {
@@ -515,13 +515,26 @@
     return nullptr;
   }
 
+  auto it = std::find_if(discovered_ble_peripherals_map_.begin(),
+                         discovered_ble_peripherals_map_.end(),
+                         [&peripheral_id](const auto& address_device_pair) {
+                           return address_device_pair.second.GetUniqueId() ==
+                                  peripheral_id;
+                         });
+
+  if (it == discovered_ble_peripherals_map_.end()) {
+    LOG(WARNING) << __func__
+                 << ": no match for device at peripheral_id=" << peripheral_id;
+    return nullptr;
+  }
+
   base::WaitableEvent connect_to_gatt_server_waitable_event;
   CHECK(adapter_.is_bound());
   mojo::PendingRemote<bluetooth::mojom::Device> device;
   task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&BleV2Medium::DoConnectToGattServer,
-                     base::Unretained(this), &device, peripheral.GetAddress(),
+                     base::Unretained(this), &device, it->second.GetAddress(),
                      &connect_to_gatt_server_waitable_event));
   base::ScopedAllowBaseSyncPrimitives allow_wait;
   connect_to_gatt_server_waitable_event.Wait();
@@ -559,7 +572,7 @@
 std::unique_ptr<api::ble_v2::BleSocket> BleV2Medium::Connect(
     const std::string& service_id,
     api::ble_v2::TxPowerLevel tx_power_level,
-    api::ble_v2::BlePeripheral& peripheral,
+    api::ble_v2::BlePeripheral::UniqueId peripheral_id,
     CancellationFlag* cancellation_flag) {
   NOTIMPLEMENTED();
   return nullptr;
@@ -580,24 +593,6 @@
   return success && info->extended_advertisement_support;
 }
 
-bool BleV2Medium::GetRemotePeripheral(api::ble_v2::BlePeripheral::UniqueId id,
-                                      GetRemotePeripheralCallback callback) {
-  auto it =
-      std::find_if(discovered_ble_peripherals_map_.begin(),
-                   discovered_ble_peripherals_map_.end(),
-                   [&](const auto& address_device_pair) {
-                     return address_device_pair.second.GetUniqueId() == id;
-                   });
-
-  if (it == discovered_ble_peripherals_map_.end()) {
-    LOG(WARNING) << __func__ << ": no match for device at id = " << id;
-    return false;
-  }
-
-  std::move(callback)(it->second);
-  return true;
-}
-
 void BleV2Medium::PresentChanged(bool present) {
   NOTIMPLEMENTED();
 }
@@ -687,7 +682,7 @@
 
       if (scanning_callback_iter->second.advertisement_found_cb) {
         scanning_callback_iter->second.advertisement_found_cb(
-            *ble_peripheral, advertisement_data);
+            ble_peripheral->GetUniqueId(), advertisement_data);
       }
     }
   }
diff --git a/chrome/services/sharing/nearby/platform/ble_v2_medium.h b/chrome/services/sharing/nearby/platform/ble_v2_medium.h
index d244234..dede8577 100644
--- a/chrome/services/sharing/nearby/platform/ble_v2_medium.h
+++ b/chrome/services/sharing/nearby/platform/ble_v2_medium.h
@@ -80,7 +80,7 @@
       api::ble_v2::ServerGattConnectionCallback callback) override;
 
   std::unique_ptr<api::ble_v2::GattClient> ConnectToGattServer(
-      api::ble_v2::BlePeripheral& peripheral,
+      api::ble_v2::BlePeripheral::UniqueId peripheral_id,
       api::ble_v2::TxPowerLevel tx_power_level,
       api::ble_v2::ClientGattConnectionCallback callback) override;
 
@@ -90,14 +90,11 @@
   std::unique_ptr<api::ble_v2::BleSocket> Connect(
       const std::string& service_id,
       api::ble_v2::TxPowerLevel tx_power_level,
-      api::ble_v2::BlePeripheral& peripheral,
+      api::ble_v2::BlePeripheral::UniqueId peripheral_id,
       CancellationFlag* cancellation_flag) override;
 
   bool IsExtendedAdvertisementsAvailable() override;
 
-  bool GetRemotePeripheral(api::ble_v2::BlePeripheral::UniqueId id,
-                           GetRemotePeripheralCallback callback) override;
-
  private:
   // TODO(b/330759317): Remove FRIEND call once unit tests can rely on
   // Fake classes instead of accessing private members.
diff --git a/chrome/services/sharing/nearby/platform/ble_v2_medium_unittest.cc b/chrome/services/sharing/nearby/platform/ble_v2_medium_unittest.cc
index b2d8e0a..b8025002 100644
--- a/chrome/services/sharing/nearby/platform/ble_v2_medium_unittest.cc
+++ b/chrome/services/sharing/nearby/platform/ble_v2_medium_unittest.cc
@@ -27,7 +27,7 @@
 
 namespace {
 
-const char kDeviceAddress[] = "DeviceAddress";
+const char kDeviceAddress[] = "11:12:13:14:15:16";
 const char kDeviceServiceData1Str[] = "Device_Advertisement1";
 const char kDeviceServiceData2Str[] = "Device_Advertisement2";
 const ByteArray kDeviceServiceData1ByteArray{
@@ -46,7 +46,7 @@
     kTestServiceUuid2.data().size())};
 const char kServiceId[] = "TestServiceId";
 const char kCharacteristicUuid[] = "1234";
-const uint64_t kUniqueId = 1053256082272529;
+const uint64_t kUniqueId = 24279786918417;
 
 std::vector<uint8_t> GetByteVector(const std::string& str) {
   return std::vector<uint8_t>(str.begin(), str.end());
@@ -160,7 +160,7 @@
     base::ScopedAllowBaseSyncPrimitivesForTesting allow_sync_primitives;
     FakeBleV2RemotePeripheral peripheral;
     auto gatt_client = ble_v2_medium_->ConnectToGattServer(
-        peripheral, api::ble_v2::TxPowerLevel::kHigh,
+        peripheral.GetUniqueId(), api::ble_v2::TxPowerLevel::kHigh,
         /*callback=*/{});
     EXPECT_EQ(expected_success, (gatt_client != nullptr));
   }
@@ -192,21 +192,14 @@
           },
       .advertisement_found_cb =
           [this, &found_advertisement_latch](
-              api::ble_v2::BlePeripheral& peripheral,
+              api::ble_v2::BlePeripheral::UniqueId peripheral_id,
               const api::ble_v2::BleAdvertisementData& advertisement_data) {
-            EXPECT_EQ(peripheral.GetAddress(), kDeviceAddress);
             EXPECT_EQ(advertisement_data.service_data
                           .find(kFastAdvertisementServiceUuid1)
                           ->second,
                       kDeviceServiceData1ByteArray);
             found_advertisement_latch.CountDown();
             OnPeripheralDiscovered();
-
-            EXPECT_TRUE(ble_v2_medium_->GetRemotePeripheral(
-                peripheral.GetUniqueId(),
-                [&](api::ble_v2::BlePeripheral& device) {
-                  EXPECT_EQ(kDeviceAddress, device.GetAddress());
-                }));
           }};
 
   auto scanning_session = ble_v2_medium_->StartScanning(
@@ -252,16 +245,10 @@
           },
       .advertisement_found_cb =
           [this, &session_1_found_advertisement_latch](
-              api::ble_v2::BlePeripheral& peripheral,
+              api::ble_v2::BlePeripheral::UniqueId peripheral_id,
               const api::ble_v2::BleAdvertisementData& advertisement_data) {
             session_1_found_advertisement_latch.CountDown();
             OnPeripheralDiscovered();
-
-            EXPECT_TRUE(ble_v2_medium_->GetRemotePeripheral(
-                peripheral.GetUniqueId(),
-                [&](api::ble_v2::BlePeripheral& device) {
-                  EXPECT_EQ(kDeviceAddress, device.GetAddress());
-                }));
           }};
   api::ble_v2::BleMedium::ScanningCallback scanning_callback_2 = {
       .start_scanning_result =
@@ -270,16 +257,10 @@
           },
       .advertisement_found_cb =
           [this, &session_2_found_advertisement_latch](
-              api::ble_v2::BlePeripheral& peripheral,
+              api::ble_v2::BlePeripheral::UniqueId peripheral_id,
               const api::ble_v2::BleAdvertisementData& advertisement_data) {
             session_2_found_advertisement_latch.CountDown();
             OnPeripheralDiscovered();
-
-            EXPECT_TRUE(ble_v2_medium_->GetRemotePeripheral(
-                peripheral.GetUniqueId(),
-                [&](api::ble_v2::BlePeripheral& device) {
-                  EXPECT_EQ(kDeviceAddress, device.GetAddress());
-                }));
           }};
 
   auto scanning_session_1 = ble_v2_medium_->StartScanning(
@@ -333,7 +314,7 @@
             scanning_started_latch.CountDown();
           },
       .advertisement_found_cb =
-          [](api::ble_v2::BlePeripheral& peripheral,
+          [](api::ble_v2::BlePeripheral::UniqueId peripheral_id,
              const api::ble_v2::BleAdvertisementData& advertisement_data) {
             // should not reached here for irrelavant advertisement.
             EXPECT_TRUE(false);
@@ -845,9 +826,47 @@
       /*enabled_features=*/{::features::kEnableNearbyBleV2},
       /*disabled_features=*/{});
 
+  CountDownLatch scanning_started_latch(1);
+  CountDownLatch found_advertisement_latch(1);
+  api::ble_v2::BleMedium::ScanningCallback scanning_callback = {
+      .start_scanning_result =
+          [&scanning_started_latch](absl::Status status) {
+            scanning_started_latch.CountDown();
+          },
+      .advertisement_found_cb =
+          [this, &found_advertisement_latch](
+              api::ble_v2::BlePeripheral::UniqueId peripheral_id,
+              const api::ble_v2::BleAdvertisementData& advertisement_data) {
+            EXPECT_EQ(advertisement_data.service_data
+                          .find(kFastAdvertisementServiceUuid1)
+                          ->second,
+                      kDeviceServiceData1ByteArray);
+            found_advertisement_latch.CountDown();
+            OnPeripheralDiscovered();
+          }};
+
+  auto scanning_session = ble_v2_medium_->StartScanning(
+      kFastAdvertisementServiceUuid1, {}, std::move(scanning_callback));
+
+  base::flat_map<device::BluetoothUUID, std::vector<uint8_t>> service_data_map;
+  service_data_map.insert_or_assign(kService1BluetoothUuid,
+                                    GetByteVector(kDeviceServiceData1Str));
+
   fake_adapter_->SetConnectToDeviceResult(
       bluetooth::mojom::ConnectResult::SUCCESS,
       std::make_unique<bluetooth::FakeDevice>());
+
+  EXPECT_TRUE(scanning_started_latch.Await().Ok());
+
+  base::RunLoop discovered_run_loop;
+  SetOnExpectedPeripheralsDiscoveredCallback(discovered_run_loop.QuitClosure());
+  fake_adapter_->NotifyDeviceAdded(
+      CreateDeviceInfo(kDeviceAddress, service_data_map));
+  discovered_run_loop.Run();
+
+  EXPECT_TRUE(found_advertisement_latch.Await().Ok());
+  EXPECT_TRUE(scanning_session->stop_scanning().ok());
+
   base::RunLoop run_loop;
   base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})
       ->PostTaskAndReply(
@@ -871,6 +890,48 @@
 
   fake_adapter_->SetConnectToDeviceResult(
       bluetooth::mojom::ConnectResult::FAILED, /*fake_device=*/nullptr);
+
+  CountDownLatch scanning_started_latch(1);
+  CountDownLatch found_advertisement_latch(1);
+  api::ble_v2::BleMedium::ScanningCallback scanning_callback = {
+      .start_scanning_result =
+          [&scanning_started_latch](absl::Status status) {
+            scanning_started_latch.CountDown();
+          },
+      .advertisement_found_cb =
+          [this, &found_advertisement_latch](
+              api::ble_v2::BlePeripheral::UniqueId peripheral_id,
+              const api::ble_v2::BleAdvertisementData& advertisement_data) {
+            EXPECT_EQ(advertisement_data.service_data
+                          .find(kFastAdvertisementServiceUuid1)
+                          ->second,
+                      kDeviceServiceData1ByteArray);
+            found_advertisement_latch.CountDown();
+            OnPeripheralDiscovered();
+          }};
+
+  auto scanning_session = ble_v2_medium_->StartScanning(
+      kFastAdvertisementServiceUuid1, {}, std::move(scanning_callback));
+
+  base::flat_map<device::BluetoothUUID, std::vector<uint8_t>> service_data_map;
+  service_data_map.insert_or_assign(kService1BluetoothUuid,
+                                    GetByteVector(kDeviceServiceData1Str));
+
+  fake_adapter_->SetConnectToDeviceResult(
+      bluetooth::mojom::ConnectResult::FAILED,
+      /*fake_device=*/nullptr);
+
+  EXPECT_TRUE(scanning_started_latch.Await().Ok());
+
+  base::RunLoop discovered_run_loop;
+  SetOnExpectedPeripheralsDiscoveredCallback(discovered_run_loop.QuitClosure());
+  fake_adapter_->NotifyDeviceAdded(
+      CreateDeviceInfo(kDeviceAddress, service_data_map));
+  discovered_run_loop.Run();
+
+  EXPECT_TRUE(found_advertisement_latch.Await().Ok());
+  EXPECT_TRUE(scanning_session->stop_scanning().ok());
+
   base::RunLoop run_loop;
   base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})
       ->PostTaskAndReply(
diff --git a/chrome/services/sharing/nearby/platform/ble_v2_server_socket.cc b/chrome/services/sharing/nearby/platform/ble_v2_server_socket.cc
index ed315bb4..81e6c75 100644
--- a/chrome/services/sharing/nearby/platform/ble_v2_server_socket.cc
+++ b/chrome/services/sharing/nearby/platform/ble_v2_server_socket.cc
@@ -57,11 +57,13 @@
   return {Exception::kSuccess};
 }
 
-::nearby::api::ble_v2::BlePeripheral* BleV2Socket::GetRemotePeripheral() {
-  // Left deliberately unimplemented.
+::nearby::api::ble_v2::BlePeripheral::UniqueId
+BleV2Socket::GetRemotePeripheralId() {
+  // Although this appears to be implemented, this is incorrect and only left
+  // in this state for overriding and testing purposes.
   auto device_info = bluetooth::mojom::DeviceInfo::New();
   peripheral_ = std::make_unique<BleV2RemotePeripheral>(std::move(device_info));
-  return peripheral_.get();
+  return peripheral_->GetUniqueId();
 }
 
 // =================BleV2ServerSocket=================
diff --git a/chrome/services/sharing/nearby/platform/ble_v2_server_socket.h b/chrome/services/sharing/nearby/platform/ble_v2_server_socket.h
index ff47a556..1c635ba 100644
--- a/chrome/services/sharing/nearby/platform/ble_v2_server_socket.h
+++ b/chrome/services/sharing/nearby/platform/ble_v2_server_socket.h
@@ -52,7 +52,8 @@
   InputStream& GetInputStream() override;
   OutputStream& GetOutputStream() override;
   Exception Close() override;
-  ::nearby::api::ble_v2::BlePeripheral* GetRemotePeripheral() override;
+  ::nearby::api::ble_v2::BlePeripheral::UniqueId GetRemotePeripheralId()
+      override;
 
  private:
   std::unique_ptr<InputStream> input_stream_;
diff --git a/chrome/services/sharing/nearby/platform/ble_v2_server_socket_unittest.cc b/chrome/services/sharing/nearby/platform/ble_v2_server_socket_unittest.cc
index 6c87168e..dbc47072 100644
--- a/chrome/services/sharing/nearby/platform/ble_v2_server_socket_unittest.cc
+++ b/chrome/services/sharing/nearby/platform/ble_v2_server_socket_unittest.cc
@@ -13,6 +13,8 @@
 
 namespace nearby::chrome {
 
+const uint64_t kUniqueId = 0L;
+
 class BleV2ServerSocketTest : public testing::Test {
  public:
   BleV2ServerSocketTest() = default;
@@ -117,8 +119,8 @@
   EXPECT_TRUE(ble_v2_socket_->Close().Ok());
 }
 
-TEST_F(BleV2ServerSocketTest, Socket_GetRemotePeripheral) {
-  EXPECT_TRUE(ble_v2_socket_->GetRemotePeripheral());
+TEST_F(BleV2ServerSocketTest, Socket_GetRemotePeripheralId) {
+  EXPECT_EQ(ble_v2_socket_->GetRemotePeripheralId(), kUniqueId);
 }
 
 TEST_F(BleV2ServerSocketTest, InputStream_Read) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index e6566c5..af91fe1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3264,9 +3264,6 @@
       "../browser/preloading/prefetch/search_prefetch/search_prefetch_browser_test_base.h",
       "../browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc",
       "../browser/preloading/prefetch/zero_suggest_prefetch/zero_suggest_prefetch_tab_helper_browsertest.cc",
-      "../browser/private_network_access/chrome_private_network_access_browsertest.cc",
-      "../browser/private_network_access/private_network_device_browser_test_utils.cc",
-      "../browser/private_network_access/private_network_device_browser_test_utils.h",
       "../browser/profile_resetter/profile_resetter_browsertest.cc",
       "../browser/profiles/host_zoom_map_browsertest.cc",
       "../browser/profiles/incognito_profile_containment_browsertest.cc",
@@ -8085,6 +8082,7 @@
       "//chrome/browser/ui/webui:webui_util",
       "//chrome/browser/ui/webui/access_code_cast:unit_tests",
       "//chrome/browser/ui/webui/app_management:unit_tests",
+      "//chrome/browser/ui/webui/customize_buttons",
       "//chrome/browser/ui/webui/new_tab_footer:unit_tests",
       "//chrome/browser/ui/webui/privacy_sandbox:unit_tests",
       "//chrome/browser/ui/webui/settings",
@@ -8302,6 +8300,7 @@
       "//chrome/browser/ui/safety_hub:unit_tests",
       "//chrome/browser/ui/signin",
       "//chrome/browser/ui/tab_contents:unit_tests",
+      "//chrome/browser/ui/tabs/alert:unit_tests",
       "//chrome/browser/ui/toasts",
       "//chrome/browser/ui/toasts:unit_tests",
       "//chrome/browser/ui/toasts/api:unit_tests",
@@ -8969,9 +8968,12 @@
       "../browser/extensions/install_tracker_unittest.cc",
       "../browser/extensions/install_verifier_unittest.cc",
       "../browser/extensions/installed_loader_unittest.cc",
+      "../browser/extensions/permissions/active_tab_unittest.cc",
+      "../browser/extensions/permissions/site_permissions_helper_unittest.cc",
       "../browser/extensions/permissions_based_management_policy_provider_unittest.cc",
       "../browser/extensions/policy_handlers_unittest.cc",
       "../browser/extensions/standard_management_policy_provider_unittest.cc",
+      "../browser/extensions/tab_helper_unittest.cc",
       "../browser/extensions/unpacked_installer_unittest.cc",
       "../browser/extensions/updater/extension_update_client_command_line_config_policy_unittest.cc",
       "../browser/extensions/updater/extension_updater_switches_unittest.cc",
@@ -9111,6 +9113,7 @@
       "../browser/extensions/api/tabs/tabs_api_unittest.cc",
       "../browser/extensions/api/tabs/windows_util_unittest.cc",
       "../browser/extensions/api/web_navigation/frame_navigation_state_unittest.cc",
+      "../browser/extensions/app_tab_helper_unittest.cc",
       "../browser/extensions/blocklist_check_unittest.cc",
       "../browser/extensions/blocklist_extension_prefs_unittest.cc",
       "../browser/extensions/blocklist_state_fetcher_unittest.cc",
@@ -9153,17 +9156,14 @@
       "../browser/extensions/pack_extension_unittest.cc",
       "../browser/extensions/permission_message_combinations_unittest.cc",
       "../browser/extensions/permission_messages_unittest.cc",
-      "../browser/extensions/permissions/active_tab_unittest.cc",
       "../browser/extensions/permissions/host_access_requests_helper_unittest.cc",
       "../browser/extensions/permissions/permissions_manager_unittest.cc",
       "../browser/extensions/permissions/permissions_updater_unittest.cc",
       "../browser/extensions/permissions/scripting_permissions_modifier_unittest.cc",
-      "../browser/extensions/permissions/site_permissions_helper_unittest.cc",
       "../browser/extensions/preinstalled_apps_unittest.cc",
       "../browser/extensions/profile_util_unittest.cc",
       "../browser/extensions/safe_browsing_verdict_handler_unittest.cc",
       "../browser/extensions/shared_module_service_unittest.cc",
-      "../browser/extensions/tab_helper_unittest.cc",
       "../browser/extensions/update_install_gate_unittest.cc",
       "../browser/extensions/user_script_listener_unittest.cc",
       "../browser/extensions/user_script_world_configuration_manager_unittest.cc",
@@ -11034,6 +11034,8 @@
       # c/b/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_interactive_ui_test.cc
       # gets modularized.
       "//chrome/browser/privacy_sandbox:test_support",
+      "//chrome/browser/privacy_sandbox/notice:factory",
+      "//chrome/browser/privacy_sandbox/notice:test_support",
       "//chrome/browser/search",
       "//chrome/browser/search_engines",
       "//chrome/browser/signin",
diff --git a/chrome/test/chromedriver/session.h b/chrome/test/chromedriver/session.h
index f3b18968..227c7d1 100644
--- a/chrome/test/chromedriver/session.h
+++ b/chrome/test/chromedriver/session.h
@@ -7,7 +7,6 @@
 
 #include <list>
 #include <memory>
-#include <queue>
 #include <string>
 #include <vector>
 
diff --git a/chrome/test/data/pdf/ink2_text_box_test.ts b/chrome/test/data/pdf/ink2_text_box_test.ts
index 70b9cb4..bb7a580 100644
--- a/chrome/test/data/pdf/ink2_text_box_test.ts
+++ b/chrome/test/data/pdf/ink2_text_box_test.ts
@@ -940,40 +940,45 @@
     chrome.test.succeed();
   },
 
-  async function testMoveViewportOnFocus() {
-    // Ensure the viewport is scrollable by zooming in.
-    viewport.setZoom(2.0);
+  /*
+    // TODO(crbug.com/418642851): Deflake and re-enable.
+    async function testMoveViewportOnFocus() {
+      // Ensure the viewport is scrollable by zooming in.
+      viewport.setZoom(2.0);
 
-    // Using manager initialization to get correct coordinates for the zoom
-    // level.
-    manager.initializeTextAnnotation({x: 20, y: 20});
-    await microtasksFinished();
-    const styles = getComputedStyle(textbox);
-    chrome.test.assertEq('20px', styles.getPropertyValue('left'));
-    chrome.test.assertEq('20px', styles.getPropertyValue('top'));
+      // Using manager initialization to get correct coordinates for the zoom
+      // level.
+      manager.initializeTextAnnotation({x: 20, y: 20});
+      await microtasksFinished();
+      const styles = getComputedStyle(textbox);
+      chrome.test.assertEq('20px', styles.getPropertyValue('left'));
+      chrome.test.assertEq('20px', styles.getPropertyValue('top'));
 
-    // Scroll away from the textbox. Note this method accepts page coordinates.
-    // Scrolling by 35 in page coordinates scrolls by 70 in screen coordinates
-    // at 2x zoom.
-    viewport.goToPageAndXy(0, 35, 35);
-    await microtasksFinished();
-    chrome.test.assertEq('-50px', styles.getPropertyValue('left'));
-    chrome.test.assertEq('-50px', styles.getPropertyValue('top'));
+      // Scroll away from the textbox. Note this method accepts page
+    coordinates.
+      // Scrolling by 35 in page coordinates scrolls by 70 in screen coordinates
+      // at 2x zoom.
+      viewport.goToPageAndXy(0, 35, 35);
+      await microtasksFinished();
+      chrome.test.assertEq('-50px', styles.getPropertyValue('left'));
+      chrome.test.assertEq('-50px', styles.getPropertyValue('top'));
 
-    // Focus the textbox, which should cause the manager to scroll the viewport.
-    // This won't actually scroll the viewport in the test, since the plugin
-    // won't send a corresponding scroll message back.
-    mockPlugin.clearMessages();
-    textbox.$.textbox.focus();
-    const syncScrollMessage = mockPlugin.findMessage('syncScrollToRemote');
-    chrome.test.assertTrue(syncScrollMessage !== undefined);
-    chrome.test.assertEq('syncScrollToRemote', syncScrollMessage.type);
-    // The box is at 20, 20 in viewport coordinates, and the viewport is 100px
-    // wide. The manager specifies a margin of 10% of the viewport when
-    // scrolling.
-    chrome.test.assertEq(10, syncScrollMessage.x);
-    chrome.test.assertEq(10, syncScrollMessage.y);
+      // Focus the textbox, which should cause the manager to scroll the
+    viewport.
+      // This won't actually scroll the viewport in the test, since the plugin
+      // won't send a corresponding scroll message back.
+      mockPlugin.clearMessages();
+      textbox.$.textbox.focus();
+      const syncScrollMessage = mockPlugin.findMessage('syncScrollToRemote');
+      chrome.test.assertTrue(syncScrollMessage !== undefined);
+      chrome.test.assertEq('syncScrollToRemote', syncScrollMessage.type);
+      // The box is at 20, 20 in viewport coordinates, and the viewport is 100px
+      // wide. The manager specifies a margin of 10% of the viewport when
+      // scrolling.
+      chrome.test.assertEq(10, syncScrollMessage.x);
+      chrome.test.assertEq(10, syncScrollMessage.y);
 
-    chrome.test.succeed();
-  },
+      chrome.test.succeed();
+    },
+  */
 ]);
diff --git a/chrome/test/data/webui/extensions/extensions_focus_test.cc b/chrome/test/data/webui/extensions/extensions_focus_test.cc
index df104642..3ef00cab 100644
--- a/chrome/test/data/webui/extensions/extensions_focus_test.cc
+++ b/chrome/test/data/webui/extensions/extensions_focus_test.cc
@@ -15,13 +15,10 @@
   }
 };
 
-// TODO(crbug.com/392777363): Make the test pass on android.
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 IN_PROC_BROWSER_TEST_F(CrExtensionsFocusTest, UninstallFocus) {
   RunTest("extensions/manager_unit_test.js",
           "runMochaTest('ExtensionManagerUnitTest', 'UninstallFocus')");
 }
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 IN_PROC_BROWSER_TEST_F(CrExtensionsFocusTest, UpdateShortcut) {
   RunTest("extensions/keyboard_shortcuts_test.js",
diff --git a/chrome/test/data/webui/new_tab_page/app_test.ts b/chrome/test/data/webui/new_tab_page/app_test.ts
index 97d01a3..8f68c93 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.ts
+++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import type {CustomizeButtonsDocumentRemote} from 'chrome://new-tab-page/customize_buttons.mojom-webui.js';
+import {CustomizeButtonsDocumentCallbackRouter, CustomizeButtonsHandlerRemote, CustomizeChromeSection, SidePanelOpenTrigger} from 'chrome://new-tab-page/customize_buttons.mojom-webui.js';
 import type {Module} from 'chrome://new-tab-page/lazy_load.js';
 import {counterfactualLoad, ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/lazy_load.js';
-import {$$, BackgroundManager, BrowserCommandProxy, CUSTOMIZE_CHROME_BUTTON_ELEMENT_ID, CustomizeDialogPage, NewTabPageProxy, NtpCustomizeChromeEntryPoint, NtpElement, VoiceAction, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$, BackgroundManager, BrowserCommandProxy, CUSTOMIZE_CHROME_BUTTON_ELEMENT_ID, CustomizeButtonsProxy, CustomizeDialogPage, NewTabPageProxy, NtpCustomizeChromeEntryPoint, NtpElement, VoiceAction, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import type {AppElement, CustomizeButtonsElement} from 'chrome://new-tab-page/new_tab_page.js';
 import type {PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
-import {CustomizeChromeSection, NtpBackgroundImageSource, PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {NtpBackgroundImageSource, PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import {Command, CommandHandlerRemote} from 'chrome://resources/js/browser_command.mojom-webui.js';
@@ -28,6 +30,8 @@
   let windowProxy: TestMock<WindowProxy>;
   let handler: TestMock<PageHandlerRemote>;
   let callbackRouterRemote: PageRemote;
+  let customizeButtonsHandler: TestMock<CustomizeButtonsHandlerRemote>;
+  let customizeButtonsCallbackRouterRemote: CustomizeButtonsDocumentRemote;
   let metrics: MetricsTracker;
   let moduleRegistry: TestMock<ModuleRegistry>;
   let backgroundManager: TestMock<BackgroundManager>;
@@ -43,6 +47,10 @@
     handler = installMock(
         PageHandlerRemote,
         mock => NewTabPageProxy.setInstance(mock, new PageCallbackRouter()));
+    customizeButtonsHandler = installMock(
+        CustomizeButtonsHandlerRemote,
+        mock => CustomizeButtonsProxy.setInstance(
+            mock, new CustomizeButtonsDocumentCallbackRouter()));
     handler.setResultFor('getMostVisitedSettings', Promise.resolve({
       customLinksEnabled: false,
       shortcutsVisible: false,
@@ -66,6 +74,9 @@
     windowProxy.setResultFor('url', url);
     callbackRouterRemote = NewTabPageProxy.getInstance()
                                .callbackRouter.$.bindNewPipeAndPassRemote();
+    customizeButtonsCallbackRouterRemote =
+        CustomizeButtonsProxy.getInstance()
+            .callbackRouter.$.bindNewPipeAndPassRemote();
     backgroundManager = installMock(BackgroundManager);
     backgroundManager.setResultFor(
         'getBackgroundImageLoadTime', Promise.resolve(backgroundImageLoadTime));
@@ -187,8 +198,9 @@
       assertFalse(webstoreToast.open);
 
       // Open the side panel.
-      callbackRouterRemote.setCustomizeChromeSidePanelVisibility(true);
-      await callbackRouterRemote.$.flushForTesting();
+      customizeButtonsCallbackRouterRemote
+          .setCustomizeChromeSidePanelVisibility(true);
+      await customizeButtonsCallbackRouterRemote.$.flushForTesting();
 
       // Try to show webstore toast again.
       callbackRouterRemote.showWebstoreToast();
@@ -828,47 +840,64 @@
 
       // Assert.
       assertDeepEquals(
-          [true, CustomizeChromeSection.kUnspecified],
-          handler.getArgs('setCustomizeChromeSidePanelVisible')[0]);
+          [
+            true,
+            CustomizeChromeSection.kUnspecified,
+            SidePanelOpenTrigger.kNewTabPage,
+          ],
+          customizeButtonsHandler.getArgs(
+              'setCustomizeChromeSidePanelVisible')[0]);
       assertEquals(
           1,
           metrics.count(
               'NewTabPage.CustomizeChromeOpened',
               NtpCustomizeChromeEntryPoint.CUSTOMIZE_BUTTON));
       assertEquals(
-          1, handler.getCallCount('incrementCustomizeChromeButtonOpenCount'));
+          1,
+          customizeButtonsHandler.getCallCount(
+              'incrementCustomizeChromeButtonOpenCount'));
     });
 
     test('clicking customize button hides side panel', async () => {
       // Act.
-      callbackRouterRemote.setCustomizeChromeSidePanelVisibility(true);
+      customizeButtonsCallbackRouterRemote
+          .setCustomizeChromeSidePanelVisibility(true);
       assertEquals(
           0,
           metrics.count(
               'NewTabPage.CustomizeChromeOpened',
               NtpCustomizeChromeEntryPoint.CUSTOMIZE_BUTTON));
-      await callbackRouterRemote.$.flushForTesting();
+      await customizeButtonsCallbackRouterRemote.$.flushForTesting();
       getCustomizeButton().click();
 
       // Assert.
       assertDeepEquals(
-          [false, CustomizeChromeSection.kUnspecified],
-          handler.getArgs('setCustomizeChromeSidePanelVisible')[0]);
+          [
+            false,
+            CustomizeChromeSection.kUnspecified,
+            SidePanelOpenTrigger.kNewTabPage,
+          ],
+          customizeButtonsHandler.getArgs(
+              'setCustomizeChromeSidePanelVisible')[0]);
       assertEquals(
           0,
           metrics.count(
               'NewTabPage.CustomizeChromeOpened',
               NtpCustomizeChromeEntryPoint.CUSTOMIZE_BUTTON));
       assertEquals(
-          0, handler.getCallCount('incrementCustomizeChromeButtonOpenCount'));
+          0,
+          customizeButtonsHandler.getCallCount(
+              'incrementCustomizeChromeButtonOpenCount'));
     });
 
     test('clicking customize button is accessible', async () => {
-      callbackRouterRemote.setCustomizeChromeSidePanelVisibility(true);
-      await callbackRouterRemote.$.flushForTesting();
+      customizeButtonsCallbackRouterRemote
+          .setCustomizeChromeSidePanelVisibility(true);
+      await customizeButtonsCallbackRouterRemote.$.flushForTesting();
       assertEquals('true', getCustomizeButton().getAttribute('aria-pressed'));
-      callbackRouterRemote.setCustomizeChromeSidePanelVisibility(false);
-      await callbackRouterRemote.$.flushForTesting();
+      customizeButtonsCallbackRouterRemote
+          .setCustomizeChromeSidePanelVisibility(false);
+      await customizeButtonsCallbackRouterRemote.$.flushForTesting();
       assertEquals('false', getCustomizeButton().getAttribute('aria-pressed'));
     });
 
@@ -885,8 +914,13 @@
 
         // Assert.
         assertDeepEquals(
-            [true, CustomizeChromeSection.kModules],
-            handler.getArgs('setCustomizeChromeSidePanelVisible')[0]);
+            [
+              true,
+              CustomizeChromeSection.kModules,
+              SidePanelOpenTrigger.kNewTabPage,
+            ],
+            customizeButtonsHandler.getArgs(
+                'setCustomizeChromeSidePanelVisible')[0]);
         assertEquals(
             1,
             metrics.count(
@@ -905,8 +939,13 @@
       test('URL opens side panel', () => {
         // Assert.
         assertDeepEquals(
-            [true, CustomizeChromeSection.kAppearance],
-            handler.getArgs('setCustomizeChromeSidePanelVisible')[0]);
+            [
+              true,
+              CustomizeChromeSection.kAppearance,
+              SidePanelOpenTrigger.kNewTabPage,
+            ],
+            customizeButtonsHandler.getArgs(
+                'setCustomizeChromeSidePanelVisible')[0]);
         assertEquals(
             1,
             metrics.count(
@@ -971,7 +1010,8 @@
       test('does not increment button shown count on startup'), () => {
         assertEquals(
             0,
-            handler.getCallCount('incrementWallpaperSearchButtonShownCount'));
+            customizeButtonsHandler.getCallCount(
+                'incrementWallpaperSearchButtonShownCount'));
       };
 
       test('wallpaper search button is not shown if it is disabled', () => {
@@ -1039,7 +1079,8 @@
       test('increments button shown count on startup'), () => {
         assertEquals(
             1,
-            handler.getCallCount('incrementWallpaperSearchButtonShownCount'));
+            customizeButtonsHandler.getCallCount(
+                'incrementWallpaperSearchButtonShownCount'));
       };
 
       test('wallpaper search button shows if it is enabled', () => {
@@ -1070,15 +1111,22 @@
       test('clicking wallpaper search button opens side panel', () => {
         getWallpaperSearchButton().click();
         assertDeepEquals(
-            [true, CustomizeChromeSection.kWallpaperSearch],
-            handler.getArgs('setCustomizeChromeSidePanelVisible')[0]);
+            [
+              true,
+              CustomizeChromeSection.kWallpaperSearch,
+              SidePanelOpenTrigger.kNewTabPage,
+            ],
+            customizeButtonsHandler.getArgs(
+                'setCustomizeChromeSidePanelVisible')[0]);
         assertEquals(
             1,
             metrics.count(
                 'NewTabPage.CustomizeChromeOpened',
                 NtpCustomizeChromeEntryPoint.WALLPAPER_SEARCH_BUTTON));
         assertEquals(
-            1, handler.getCallCount('incrementCustomizeChromeButtonOpenCount'));
+            1,
+            customizeButtonsHandler.getCallCount(
+                'incrementCustomizeChromeButtonOpenCount'));
       });
 
       test(
@@ -1086,33 +1134,45 @@
               'and hide side panel',
           async () => {
             // Open side panel to non-wallpaper search page.
-            callbackRouterRemote.setCustomizeChromeSidePanelVisibility(true);
+            customizeButtonsCallbackRouterRemote
+                .setCustomizeChromeSidePanelVisibility(true);
             assertEquals(
                 0,
                 metrics.count(
                     'NewTabPage.CustomizeChromeOpened',
                     NtpCustomizeChromeEntryPoint.WALLPAPER_SEARCH_BUTTON));
-            await callbackRouterRemote.$.flushForTesting();
+            await customizeButtonsCallbackRouterRemote.$.flushForTesting();
 
             // Clicking the wallpaper search button should navigate the side
             // panel to the wallpaper search page.
             getWallpaperSearchButton().click();
             assertDeepEquals(
-                [true, CustomizeChromeSection.kWallpaperSearch],
-                handler.getArgs('setCustomizeChromeSidePanelVisible')[0]);
+                [
+                  true,
+                  CustomizeChromeSection.kWallpaperSearch,
+                  SidePanelOpenTrigger.kNewTabPage,
+                ],
+                customizeButtonsHandler.getArgs(
+                    'setCustomizeChromeSidePanelVisible')[0]);
 
             // Clicking the wallpaper search button, when the wallpaper search
             // page is opened, should close the side panel.
             getWallpaperSearchButton().click();
             assertDeepEquals(
-                [false, CustomizeChromeSection.kUnspecified],
-                handler.getArgs('setCustomizeChromeSidePanelVisible')[1]);
+                [
+                  false,
+                  CustomizeChromeSection.kUnspecified,
+                  SidePanelOpenTrigger.kNewTabPage,
+                ],
+                customizeButtonsHandler.getArgs(
+                    'setCustomizeChromeSidePanelVisible')[1]);
           });
 
       test('wallpaper search button is accessible', async () => {
         // Open side panel to non-wallpaper search page.
-        callbackRouterRemote.setCustomizeChromeSidePanelVisibility(true);
-        await callbackRouterRemote.$.flushForTesting();
+        customizeButtonsCallbackRouterRemote
+            .setCustomizeChromeSidePanelVisibility(true);
+        await customizeButtonsCallbackRouterRemote.$.flushForTesting();
 
         // Only customize chrome button should be labeled as pressed.
         assertEquals(
@@ -1127,8 +1187,9 @@
             'true', getWallpaperSearchButton().getAttribute('aria-pressed'));
         assertEquals('true', getCustomizeButton().getAttribute('aria-pressed'));
         // Close the side panel.
-        callbackRouterRemote.setCustomizeChromeSidePanelVisibility(false);
-        await callbackRouterRemote.$.flushForTesting();
+        customizeButtonsCallbackRouterRemote
+            .setCustomizeChromeSidePanelVisibility(false);
+        await customizeButtonsCallbackRouterRemote.$.flushForTesting();
 
         // Both buttons should not be labeled as pressed.
         assertEquals(
diff --git a/chrome/test/data/webui/privacy_sandbox/base_dialog_test.ts b/chrome/test/data/webui/privacy_sandbox/base_dialog_test.ts
index a479211..f14c23e 100644
--- a/chrome/test/data/webui/privacy_sandbox/base_dialog_test.ts
+++ b/chrome/test/data/webui/privacy_sandbox/base_dialog_test.ts
@@ -103,10 +103,6 @@
     await testButtonClick(
         page, PrivacySandboxNotice.kTopicsConsentNotice,
         PrivacySandboxNoticeEvent.kOptIn, testHandler);
-    // TODO(crbug.com/417700269): Remove this once close dialog method is
-    // removed from the mojo interface and the View Manager handles closing the
-    // dialog.
-    await testHandler.whenCalled('closeDialog');
   });
 });
 
diff --git a/chrome/test/data/webui/privacy_sandbox/test_base_dialog_browser_proxy.ts b/chrome/test/data/webui/privacy_sandbox/test_base_dialog_browser_proxy.ts
index eb38aa7..989c3926 100644
--- a/chrome/test/data/webui/privacy_sandbox/test_base_dialog_browser_proxy.ts
+++ b/chrome/test/data/webui/privacy_sandbox/test_base_dialog_browser_proxy.ts
@@ -3,14 +3,20 @@
 // found in the LICENSE file.
 
 import type {BaseDialogPageHandlerInterface} from 'chrome://privacy-sandbox-base-dialog/base_dialog.mojom-webui.js';
+import {BaseDialogPageCallbackRouter} from 'chrome://privacy-sandbox-base-dialog/base_dialog.mojom-webui.js';
+import type {BaseDialogPageRemote} from 'chrome://privacy-sandbox-base-dialog/base_dialog.mojom-webui.js';
 import type {PrivacySandboxNotice, PrivacySandboxNoticeEvent} from 'chrome://privacy-sandbox-base-dialog/notice.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 export class TestBaseDialogBrowserProxy {
+  callbackRouter: BaseDialogPageCallbackRouter =
+      new BaseDialogPageCallbackRouter();
   handler: TestBaseDialogPageHandler;
+  page: BaseDialogPageRemote;
 
   constructor() {
     this.handler = new TestBaseDialogPageHandler();
+    this.page = this.callbackRouter.$.bindNewPipeAndPassRemote();
   }
 }
 
@@ -20,7 +26,6 @@
     super([
       'resizeDialog',
       'showDialog',
-      'closeDialog',
       'eventOccurred',
     ]);
   }
@@ -33,10 +38,6 @@
     this.methodCalled('showDialog');
   }
 
-  closeDialog() {
-    this.methodCalled('closeDialog');
-  }
-
   eventOccurred(
       notice: PrivacySandboxNotice, event: PrivacySandboxNoticeEvent) {
     this.methodCalled('eventOccurred', notice, event);
diff --git a/chrome/test/delayload/delayloads_unittest.cc b/chrome/test/delayload/delayloads_unittest.cc
index 0f48b35..08f465e0 100644
--- a/chrome/test/delayload/delayloads_unittest.cc
+++ b/chrome/test/delayload/delayloads_unittest.cc
@@ -504,7 +504,7 @@
       // These are not yet supported for Arm64.
       L"dxcompiler.dll", L"dxil.dll",
 #endif  // !defined(ARCH_CPU_ARM64
-      L"vk_swiftshader.dll", L"vulkan-1.dll"};
+      L"libEGL.dll", L"libGLESv2.dll", L"vk_swiftshader.dll", L"vulkan-1.dll"};
   for (const auto& dll : extra_dlls) {
     Validate(dll);
   }
diff --git a/chrome/updater/win/installer_api.cc b/chrome/updater/win/installer_api.cc
index e8980eb..544b213 100644
--- a/chrome/updater/win/installer_api.cc
+++ b/chrome/updater/win/installer_api.cc
@@ -535,10 +535,12 @@
     int exit_code = -1;
     base::TerminationStatus final_status =
         base::TerminationStatus::TERMINATION_STATUS_MAX_ENUM;
-    const bool success = base::GetAppOutputWithExitCodeAndTimeout(
+    std::ignore = base::GetAppOutputWithExitCodeAndTimeout(
         cmdline, true, nullptr, &exit_code, timeout - timer.Elapsed(), options,
         [&](std::string_view partial_output) {
-          VLOG(1) << "Installer output: " << partial_output;
+          if (!partial_output.empty()) {
+            VLOG(1) << "Installer output: " << partial_output;
+          }
 
           const int progress =
               GetInstallerProgress(app_info.scope, app_info.app_id);
@@ -547,15 +549,15 @@
         },
         &final_status);
 
-    if (!success) {
-      if (final_status ==
-          base::TerminationStatus::TERMINATION_STATUS_LAUNCH_FAILED) {
-        return InstallerResult(GOOPDATEINSTALL_E_INSTALLER_FAILED_START,
-                               HRESULTFromLastError());
-      }
-
-      VLOG(1) << "Installer timed out or abnormally terminated, final_status: "
-              << final_status;
+    if (final_status ==
+        base::TerminationStatus::TERMINATION_STATUS_LAUNCH_FAILED) {
+      VLOG(1) << "Installer failed to launch";
+      return InstallerResult(GOOPDATEINSTALL_E_INSTALLER_FAILED_START,
+                             HRESULTFromLastError());
+    }
+    if (final_status ==
+        base::TerminationStatus::TERMINATION_STATUS_STILL_RUNNING) {
+      VLOG(1) << "Installer timed out";
       return InstallerResult(GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT);
     }
 
diff --git a/chromecast/base/statistics/weighted_moving_linear_regression.h b/chromecast/base/statistics/weighted_moving_linear_regression.h
index d7b06f4..f7f14694 100644
--- a/chromecast/base/statistics/weighted_moving_linear_regression.h
+++ b/chromecast/base/statistics/weighted_moving_linear_regression.h
@@ -6,7 +6,8 @@
 #define CHROMECAST_BASE_STATISTICS_WEIGHTED_MOVING_LINEAR_REGRESSION_H_
 
 #include <stdint.h>
-#include <queue>
+
+#include <deque>
 
 #include "chromecast/base/statistics/weighted_mean.h"
 
diff --git a/chromecast/media/cma/pipeline/backend_decryptor.h b/chromecast/media/cma/pipeline/backend_decryptor.h
index f0582b6..4a641205 100644
--- a/chromecast/media/cma/pipeline/backend_decryptor.h
+++ b/chromecast/media/cma/pipeline/backend_decryptor.h
@@ -6,7 +6,6 @@
 #define CHROMECAST_MEDIA_CMA_PIPELINE_BACKEND_DECRYPTOR_H_
 
 #include <memory>
-#include <queue>
 
 #include "base/functional/callback.h"
 #include "base/memory/ref_counted.h"
diff --git a/chromeos/ash/components/audio/cras_audio_handler.h b/chromeos/ash/components/audio/cras_audio_handler.h
index f5000659..3feec49 100644
--- a/chromeos/ash/components/audio/cras_audio_handler.h
+++ b/chromeos/ash/components/audio/cras_audio_handler.h
@@ -10,7 +10,6 @@
 
 #include <cstdint>
 #include <optional>
-#include <queue>
 #include <string>
 #include <vector>
 
diff --git a/chromeos/ash/components/boca/boca_app_client.cc b/chromeos/ash/components/boca/boca_app_client.cc
index 30e9f18d..9ac4973 100644
--- a/chromeos/ash/components/boca/boca_app_client.cc
+++ b/chromeos/ash/components/boca/boca_app_client.cc
@@ -45,6 +45,10 @@
 
 void BocaAppClient::LaunchApp() {}
 
+bool BocaAppClient::HasApp() {
+  return false;
+}
+
 void BocaAppClient::AddSessionManager(BocaSessionManager* session_manager) {
   // Session manager is created as profile service upon signin, so we can always
   // guarantee the active profile identity matches the session manager identity.
diff --git a/chromeos/ash/components/boca/boca_app_client.h b/chromeos/ash/components/boca/boca_app_client.h
index 2ec3004b..88e2647 100644
--- a/chromeos/ash/components/boca/boca_app_client.h
+++ b/chromeos/ash/components/boca/boca_app_client.h
@@ -40,6 +40,10 @@
   // Launch Boca App.
   virtual void LaunchApp();
 
+  // Find if there is any open app. Will return not if the app is already
+  // scheduled to be closed.
+  virtual bool HasApp();
+
   // Add `BocaSessionManager` instance for the current profile.
   virtual void AddSessionManager(BocaSessionManager* session_manager);
 
diff --git a/chromeos/ash/components/boca/session_api/session_client_impl.cc b/chromeos/ash/components/boca/session_api/session_client_impl.cc
index 500d0857..a377cff 100644
--- a/chromeos/ash/components/boca/session_api/session_client_impl.cc
+++ b/chromeos/ash/components/boca/session_api/session_client_impl.cc
@@ -96,6 +96,18 @@
 
 void SessionClientImpl::UpdateStudentActivity(
     std::unique_ptr<UpdateStudentActivitiesRequest> request) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (features::IsBocaSequentialInsertActivityEnabled()) {
+    if (has_blocking_update_activity_request_) {
+      pending_update_student_activity_request_ = std::move(request);
+      return;
+    }
+
+    request->set_callback(
+        base::BindOnce(&SessionClientImpl::OnInsertStudentActivityCompleted,
+                       weak_ptr_factory_.GetWeakPtr(), request->callback()));
+    has_blocking_get_session_request_ = true;
+  }
   sender_->StartRequestWithAuthRetry(std::move(request));
 }
 
@@ -123,6 +135,22 @@
   sender_->StartRequestWithAuthRetry(std::move(request));
 }
 
+void SessionClientImpl::OnInsertStudentActivityCompleted(
+    UpdateStudentActivitiesCallback callback,
+    base::expected<bool, google_apis::ApiErrorCode> result) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  has_blocking_update_activity_request_ = false;
+  std::move(callback).Run(std::move(result));
+  if (!pending_update_student_activity_request_) {
+    return;
+  }
+  auto request = std::move(pending_update_student_activity_request_);
+  request->set_callback(
+      base::BindOnce(&SessionClientImpl::OnInsertStudentActivityCompleted,
+                     weak_ptr_factory_.GetWeakPtr(), request->callback()));
+  sender_->StartRequestWithAuthRetry(std::move(request));
+}
+
 void SessionClientImpl::OnGetSessionCompleted(
     GetSessionCallback callback,
     base::expected<std::unique_ptr<::boca::Session>, google_apis::ApiErrorCode>
diff --git a/chromeos/ash/components/boca/session_api/session_client_impl.h b/chromeos/ash/components/boca/session_api/session_client_impl.h
index 43fe36a..76096a86 100644
--- a/chromeos/ash/components/boca/session_api/session_client_impl.h
+++ b/chromeos/ash/components/boca/session_api/session_client_impl.h
@@ -38,6 +38,8 @@
   using GetSessionCallback = base::OnceCallback<void(
       base::expected<std::unique_ptr<::boca::Session>,
                      google_apis::ApiErrorCode> result)>;
+  using UpdateStudentActivitiesCallback = base::OnceCallback<void(
+      base::expected<bool, google_apis::ApiErrorCode> result)>;
 
   SessionClientImpl();
   explicit SessionClientImpl(
@@ -66,6 +68,9 @@
   google_apis::RequestSender* sender() { return sender_.get(); }
 
  private:
+  void OnInsertStudentActivityCompleted(
+      UpdateStudentActivitiesCallback callback,
+      base::expected<bool, google_apis::ApiErrorCode> result);
   void OnGetSessionCompleted(GetSessionCallback callback,
                              base::expected<std::unique_ptr<::boca::Session>,
                                             google_apis::ApiErrorCode> result);
@@ -76,6 +81,11 @@
       GUARDED_BY_CONTEXT(sequence_checker_);
   bool has_blocking_get_session_request_ GUARDED_BY_CONTEXT(sequence_checker_) =
       false;
+
+  bool has_blocking_update_activity_request_
+      GUARDED_BY_CONTEXT(sequence_checker_) = false;
+  std::unique_ptr<UpdateStudentActivitiesRequest>
+      pending_update_student_activity_request_;
   base::WeakPtrFactory<SessionClientImpl> weak_ptr_factory_{this};
 };
 }  // namespace ash::boca
diff --git a/chromeos/ash/components/boca/session_api/session_client_impl_unittest.cc b/chromeos/ash/components/boca/session_api/session_client_impl_unittest.cc
index 54761e9..50a0366 100644
--- a/chromeos/ash/components/boca/session_api/session_client_impl_unittest.cc
+++ b/chromeos/ash/components/boca/session_api/session_client_impl_unittest.cc
@@ -11,6 +11,7 @@
 #include "chromeos/ash/components/boca/proto/session.pb.h"
 #include "chromeos/ash/components/boca/session_api/get_session_request.h"
 #include "chromeos/ash/components/boca/session_api/renotify_student_request.h"
+#include "chromeos/ash/components/boca/session_api/update_student_activities_request.h"
 #include "google_apis/common/dummy_auth_service.h"
 #include "google_apis/common/request_sender.h"
 #include "net/http/http_status_code.h"
@@ -80,6 +81,15 @@
     return request;
   }
 
+  std::unique_ptr<UpdateStudentActivitiesRequest> CreateInsertActivityRequest(
+      SessionClientImpl::UpdateStudentActivitiesCallback callback) {
+    auto request = std::make_unique<UpdateStudentActivitiesRequest>(
+        request_sender_, "https://test", "1", GaiaId("2"), "device_id",
+        std::move(callback));
+    request->OverrideURLForTesting(test_server_.base_url().spec());
+    return request;
+  }
+
   net::EmbeddedTestServer test_server_;
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::MainThreadType::IO};
@@ -160,17 +170,17 @@
   EXPECT_CALL(request_handler_, HandleRequest(_)).Times(1);
   session_client_impl_->GetSession(
       CreateGetSessionRequest(future_1.GetCallback()),
-      /*can_skip_duplicate_request=*/true);
+      /*can_skip_duplicate_request=*/false);
   EXPECT_TRUE(future_1.Wait());
   EXPECT_CALL(request_handler_, HandleRequest(_)).Times(1);
   session_client_impl_->GetSession(
       CreateGetSessionRequest(future_2.GetCallback()),
-      /*can_skip_duplicate_request=*/true);
+      /*can_skip_duplicate_request=*/false);
   EXPECT_TRUE(future_2.Wait());
   EXPECT_CALL(request_handler_, HandleRequest(_)).Times(1);
   session_client_impl_->GetSession(
       CreateGetSessionRequest(future_3.GetCallback()),
-      /*can_skip_duplicate_request=*/true);
+      /*can_skip_duplicate_request=*/false);
   EXPECT_TRUE(future_3.Wait());
 }
 
@@ -190,4 +200,34 @@
   EXPECT_TRUE(future.Wait());
 }
 
+TEST_F(SessionClientImplTest, ConcurrentInsertActivityDisallowDedupe) {
+  EXPECT_CALL(request_handler_, HandleRequest(_)).Times(2);
+  base::test::TestFuture<base::expected<bool, google_apis::ApiErrorCode>>
+      future_1;
+  base::test::TestFuture<base::expected<bool, google_apis::ApiErrorCode>>
+      future_2;
+
+  session_client_impl_->UpdateStudentActivity(
+      CreateInsertActivityRequest(future_1.GetCallback()));
+  session_client_impl_->UpdateStudentActivity(
+      CreateInsertActivityRequest(future_2.GetCallback()));
+
+  EXPECT_TRUE(future_1.Wait());
+  EXPECT_TRUE(future_2.Wait());
+}
+
+TEST_F(SessionClientImplTest, SequentialInsertActivityRequestRunInOrder) {
+  base::test::TestFuture<base::expected<bool, google_apis::ApiErrorCode>>
+      future_1;
+  base::test::TestFuture<base::expected<bool, google_apis::ApiErrorCode>>
+      future_2;
+  EXPECT_CALL(request_handler_, HandleRequest(_)).Times(1);
+  session_client_impl_->UpdateStudentActivity(
+      CreateInsertActivityRequest(future_1.GetCallback()));
+  EXPECT_TRUE(future_1.Wait());
+  EXPECT_CALL(request_handler_, HandleRequest(_)).Times(1);
+  session_client_impl_->UpdateStudentActivity(
+      CreateInsertActivityRequest(future_2.GetCallback()));
+  EXPECT_TRUE(future_2.Wait());
+}
 }  // namespace ash::boca
diff --git a/chromeos/ash/components/boca/session_api/update_student_activities_request.h b/chromeos/ash/components/boca/session_api/update_student_activities_request.h
index 69f38f1f..9c6edce 100644
--- a/chromeos/ash/components/boca/session_api/update_student_activities_request.h
+++ b/chromeos/ash/components/boca/session_api/update_student_activities_request.h
@@ -45,7 +45,9 @@
   void set_active_tab_title(std::string active_tab_title) {
     active_tab_title_ = std::move(active_tab_title);
   }
-
+  void set_callback(UpdateStudentActivitiesCallback callback) {
+    callback_ = std::move(callback);
+  }
   // For testing.
   void OverrideURLForTesting(std::string url);
 
diff --git a/chromeos/ash/components/kcer/kcer_nss/kcer_token_impl_nss.h b/chromeos/ash/components/kcer/kcer_nss/kcer_token_impl_nss.h
index a2439ec4..33c96e48 100644
--- a/chromeos/ash/components/kcer/kcer_nss/kcer_token_impl_nss.h
+++ b/chromeos/ash/components/kcer/kcer_nss/kcer_token_impl_nss.h
@@ -7,7 +7,7 @@
 
 #include <stdint.h>
 
-#include <queue>
+#include <deque>
 #include <string>
 #include <vector>
 
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt
index a1ffb65..bcd09b20 100644
--- a/chromeos/profiles/arm.afdo.newest.txt
+++ b/chromeos/profiles/arm.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-none-138-7151.8-1747016258-benchmark-138.0.7184.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-none-138-7151.22-1747621183-benchmark-138.0.7190.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index f01496f..14eb93b 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-138-7151.17-1747032091-benchmark-138.0.7180.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-138-7151.22-1747620429-benchmark-138.0.7190.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index f1d9d79..c7978181 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-138-7151.8-1747020746-benchmark-138.0.7180.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-138-7151.22-1747622342-benchmark-138.0.7190.0-r1-redacted.afdo.xz
diff --git a/clank b/clank
index 51cc999..d560887 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 51cc9995b6d55b6e3da653848c3dbae2e3d68316
+Subproject commit d56088756d87245fc8f070fc3d7d24a4a09cfc8c
diff --git a/components/autofill/core/browser/data_model/payments/credit_card_test_api.h b/components/autofill/core/browser/data_model/payments/credit_card_test_api.h
index 32d143a..1b92750 100644
--- a/components/autofill/core/browser/data_model/payments/credit_card_test_api.h
+++ b/components/autofill/core/browser/data_model/payments/credit_card_test_api.h
@@ -26,6 +26,10 @@
     credit_card_->network_ = std::string(network);
   }
 
+  // TODO(crbug.com/416338314): Remove direct benefit source getter from this
+  // test api once `kAutofillEnableCardBenefitsSourceSync` is removed.
+  const std::string& benefit_source() { return credit_card_->benefit_source_; }
+
  private:
   const raw_ref<CreditCard> credit_card_;
 };
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
index 76efbb9..9f35d1b 100644
--- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
@@ -153,9 +153,7 @@
       SuggestionType::kLoyaltyCardEntry);
   submenu_suggestion.acceptability = Suggestion::Acceptability::kUnacceptable;
   submenu_suggestion.children = loyalty_card_suggestions;
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  submenu_suggestion.icon = Suggestion::Icon::kGoogleWalletMonochrome;
-#endif
+
   // There is at least one email, separator and manage addresses suggestion.
   CHECK(email_suggestions.size() >= 3);
   email_suggestions.insert(email_suggestions.end() - 1, submenu_suggestion);
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
index 59667b9..93b07380 100644
--- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
@@ -258,8 +258,6 @@
                                        IDS_AUTOFILL_MANAGE_LOYALTY_CARDS),
                                    Suggestion::Icon::kSettings)));
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  EXPECT_THAT(lc_submenu_suggestion,
-              HasIcon(Suggestion::Icon::kGoogleWalletMonochrome));
   EXPECT_THAT(lc_submenu_suggestion.children.back(),
               HasTrailingIcon(Suggestion::Icon::kGoogleWallet));
 #endif
diff --git a/components/autofill/core/browser/webdata/payments/payments_sync_bridge_util.cc b/components/autofill/core/browser/webdata/payments/payments_sync_bridge_util.cc
index d7e73b1..3bbf68ef 100644
--- a/components/autofill/core/browser/webdata/payments/payments_sync_bridge_util.cc
+++ b/components/autofill/core/browser/webdata/payments/payments_sync_bridge_util.cc
@@ -25,6 +25,7 @@
 #include "components/autofill/core/browser/data_model/payments/iban.h"
 #include "components/autofill/core/browser/data_model/payments/payment_instrument.h"
 #include "components/autofill/core/browser/data_quality/autofill_data_util.h"
+#include "components/autofill/core/browser/payments/constants.h"
 #include "components/autofill/core/browser/payments/payments_customer_data.h"
 #include "components/autofill/core/browser/webdata/payments/payments_autofill_table.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
@@ -361,6 +362,26 @@
   }
   result.set_card_info_retrieval_enrollment_state(enrollment_state);
 
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillEnableCardBenefitsSourceSync)) {
+    std::string benefit_source;
+    switch (card.card_benefit_source()) {
+      case sync_pb::WalletMaskedCreditCard::SOURCE_UNKNOWN:
+        benefit_source = "";
+        break;
+      case sync_pb::WalletMaskedCreditCard::SOURCE_AMEX:
+        benefit_source = std::string(kAmexCardBenefitSource);
+        break;
+      case sync_pb::WalletMaskedCreditCard::SOURCE_BMO:
+        benefit_source = std::string(kBmoCardBenefitSource);
+        break;
+      case sync_pb::WalletMaskedCreditCard::SOURCE_CURINOS:
+        benefit_source = std::string(kCurinosCardBenefitSource);
+        break;
+    }
+    result.set_benefit_source(benefit_source);
+  }
+
   return result;
 }
 
@@ -583,6 +604,20 @@
       break;
   }
   wallet_card->set_card_info_retrieval_enrollment_state(enrollment_state);
+
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillEnableCardBenefitsSourceSync)) {
+    sync_pb::WalletMaskedCreditCard::CardBenefitSource benefit_source =
+        sync_pb::WalletMaskedCreditCard::SOURCE_UNKNOWN;
+    if (card.benefit_source() == kAmexCardBenefitSource) {
+      benefit_source = sync_pb::WalletMaskedCreditCard::SOURCE_AMEX;
+    } else if (card.benefit_source() == kBmoCardBenefitSource) {
+      benefit_source = sync_pb::WalletMaskedCreditCard::SOURCE_BMO;
+    } else if (card.benefit_source() == kCurinosCardBenefitSource) {
+      benefit_source = sync_pb::WalletMaskedCreditCard::SOURCE_CURINOS;
+    }
+    wallet_card->set_card_benefit_source(benefit_source);
+  }
 }
 
 void SetAutofillWalletSpecificsFromPaymentsCustomerData(
diff --git a/components/autofill/core/browser/webdata/payments/payments_sync_bridge_util_unittest.cc b/components/autofill/core/browser/webdata/payments/payments_sync_bridge_util_unittest.cc
index 0b7f700e..3229c0e 100644
--- a/components/autofill/core/browser/webdata/payments/payments_sync_bridge_util_unittest.cc
+++ b/components/autofill/core/browser/webdata/payments/payments_sync_bridge_util_unittest.cc
@@ -22,6 +22,7 @@
 #include "components/autofill/core/browser/data_model/payments/credit_card_benefit.h"
 #include "components/autofill/core/browser/data_model/payments/credit_card_benefit_test_api.h"
 #include "components/autofill/core/browser/data_model/payments/credit_card_cloud_token_data.h"
+#include "components/autofill/core/browser/data_model/payments/credit_card_test_api.h"
 #include "components/autofill/core/browser/payments/payments_customer_data.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
 #include "components/autofill/core/browser/webdata/payments/payments_autofill_table.h"
@@ -117,6 +118,9 @@
 
 // Tests that PopulateWalletTypesFromSyncData behaves as expected.
 TEST_F(PaymentsSyncBridgeUtilTest, PopulateWalletTypesFromSyncData) {
+  base::test::ScopedFeatureList feature;
+  feature.InitAndEnableFeature(features::kAutofillEnableCardBenefitsSourceSync);
+
   syncer::EntityChangeList entity_data;
   // Add two credit cards.
   std::string credit_card_id_1 = "credit_card_1";
@@ -165,6 +169,8 @@
   wallet_specifics_card2.mutable_masked_card()
       ->set_card_info_retrieval_enrollment_state(
           sync_pb::WalletMaskedCreditCard::RETRIEVAL_ENROLLED);
+  wallet_specifics_card2.mutable_masked_card()->set_card_benefit_source(
+      sync_pb::WalletMaskedCreditCard::SOURCE_AMEX);
   sync_pb::AutofillWalletSpecifics wallet_specifics_iban =
       CreateAutofillWalletSpecificsForIban(
           /*client_tag=*/iban_id);
@@ -250,6 +256,10 @@
                 kRetrievalUnenrolledAndNotEligible);
   EXPECT_EQ(wallet_cards.back().card_info_retrieval_enrollment_state(),
             CreditCard::CardInfoRetrievalEnrollmentState::kRetrievalEnrolled);
+
+  // Verify that the benefit source is set correctly.
+  EXPECT_EQ(wallet_cards.front().benefit_source(), "");
+  EXPECT_EQ(wallet_cards.back().benefit_source(), kAmexCardBenefitSource);
 }
 
 class PaymentsSyncBridgeUtilCardBenefitsTest : public testing::Test {
@@ -485,6 +495,118 @@
         CreditCardCategoryBenefit::BenefitCategory::kWholesaleClubs,
         CreditCardCategoryBenefit::BenefitCategory::kUnknownBenefitCategory));
 
+// Test suite for masked card syncing helpers that takes a boolean indicating
+// the feature flag status, benefit source wallet specifics enum and the
+// matching benefit source string.
+class PaymentsSyncBridgeUtilCardBenefitsSourceSyncTest
+    : public testing::TestWithParam<
+          std::tuple<bool,
+                     sync_pb::WalletMaskedCreditCard::CardBenefitSource,
+                     std::string_view>> {
+ public:
+  PaymentsSyncBridgeUtilCardBenefitsSourceSyncTest() {
+    feature_list_.InitWithFeatureState(
+        features::kAutofillEnableCardBenefitsSourceSync,
+        IsSyncingFlagEnabled());
+  }
+
+  syncer::EntityChangeList PrepareSyncDataWithBenefitSource() {
+    sync_pb::AutofillWalletSpecifics wallet_specifics_card =
+        CreateAutofillWalletSpecificsForCard(
+            /*client_tag=*/"credit_card_0",
+            /*billing_address_id=*/"0");
+    wallet_specifics_card.mutable_masked_card()->set_instrument_id(1234);
+    wallet_specifics_card.mutable_masked_card()->set_card_benefit_source(
+        GetBenefitSourceForSpecifics());
+
+    syncer::EntityChangeList entity_data;
+    entity_data.push_back(EntityChange::CreateAdd(
+        wallet_specifics_card.mutable_masked_card()->id(),
+        SpecificsToEntity(wallet_specifics_card, /*client_tag=*/"card-card0")));
+
+    return entity_data;
+  }
+
+  bool IsSyncingFlagEnabled() { return std::get<0>(GetParam()); }
+
+  sync_pb::WalletMaskedCreditCard::CardBenefitSource
+  GetBenefitSourceForSpecifics() {
+    return std::get<1>(GetParam());
+  }
+
+  std::string_view GetBenefitSourceString() { return std::get<2>(GetParam()); }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Initializes the parameterized test suite with a boolean indicate whether the
+// feature flag is enabled,
+// `sync_pb::WalletMaskedCreditCard::CardBenefitSource` enum and the
+// corresponding benefit source string.
+INSTANTIATE_TEST_SUITE_P(
+    /*no prefix*/,
+    PaymentsSyncBridgeUtilCardBenefitsSourceSyncTest,
+    testing::Values(
+        std::make_tuple(true,
+                        sync_pb::WalletMaskedCreditCard::SOURCE_UNKNOWN,
+                        ""),
+        std::make_tuple(true,
+                        sync_pb::WalletMaskedCreditCard::SOURCE_AMEX,
+                        kAmexCardBenefitSource),
+        std::make_tuple(true,
+                        sync_pb::WalletMaskedCreditCard::SOURCE_BMO,
+                        kBmoCardBenefitSource),
+        std::make_tuple(true,
+                        sync_pb::WalletMaskedCreditCard::SOURCE_CURINOS,
+                        kCurinosCardBenefitSource),
+        std::make_tuple(false,
+                        sync_pb::WalletMaskedCreditCard::SOURCE_UNKNOWN,
+                        ""),
+        std::make_tuple(false,
+                        sync_pb::WalletMaskedCreditCard::SOURCE_AMEX,
+                        kAmexCardBenefitSource),
+        std::make_tuple(false,
+                        sync_pb::WalletMaskedCreditCard::SOURCE_BMO,
+                        kBmoCardBenefitSource),
+        std::make_tuple(false,
+                        sync_pb::WalletMaskedCreditCard::SOURCE_CURINOS,
+                        kCurinosCardBenefitSource)),
+    [](const testing::TestParamInfo<
+        PaymentsSyncBridgeUtilCardBenefitsSourceSyncTest::ParamType>& info) {
+      return base::StrCat({std::get<0>(info.param) ? "BenefitSourceEnabled_"
+                                                   : "BenefitSourceDisabled_",
+                           std::get<2>(info.param).empty()
+                               ? "SourceUnknown"
+                               : std::get<2>(info.param)});
+    });
+
+// Tests that when `kAutofillEnableCardBenefitsSourceSync` is enabled, benefit
+// source will set based on the benefit source enum from the synced data.
+// When the flag is disabled, benefit source will not be set.
+TEST_P(PaymentsSyncBridgeUtilCardBenefitsSourceSyncTest, BenefitSourceMapping) {
+  // Add a card with benefit source to entity.
+  syncer::EntityChangeList entity_data = PrepareSyncDataWithBenefitSource();
+
+  std::vector<CreditCard> wallet_cards;
+  std::vector<Iban> wallet_ibans;
+  std::vector<PaymentsCustomerData> customer_data;
+  std::vector<CreditCardCloudTokenData> cloud_token_data;
+  std::vector<BankAccount> bank_accounts;
+  std::vector<CreditCardBenefit> benefits;
+  std::vector<sync_pb::PaymentInstrument> payment_instruments;
+  std::vector<sync_pb::PaymentInstrumentCreationOption>
+      payment_instrument_creation_options;
+  PopulateWalletTypesFromSyncData(entity_data, wallet_cards, wallet_ibans,
+                                  customer_data, cloud_token_data,
+                                  bank_accounts, benefits, payment_instruments,
+                                  payment_instrument_creation_options);
+
+  ASSERT_EQ(1U, wallet_cards.size());
+  EXPECT_EQ(test_api(wallet_cards.front()).benefit_source(),
+            IsSyncingFlagEnabled() ? GetBenefitSourceString() : std::string());
+}
+
 // Verify that the billing address id from the card saved on disk is kept if it
 // is a local profile guid.
 TEST_F(PaymentsSyncBridgeUtilTest,
diff --git a/components/browser_ui/util/android/BUILD.gn b/components/browser_ui/util/android/BUILD.gn
index 2e7c4137..c2ab19c 100644
--- a/components/browser_ui/util/android/BUILD.gn
+++ b/components/browser_ui/util/android/BUILD.gn
@@ -24,6 +24,7 @@
     "java/src/org/chromium/components/browser_ui/util/date/CalendarFactory.java",
     "java/src/org/chromium/components/browser_ui/util/date/CalendarUtils.java",
     "java/src/org/chromium/components/browser_ui/util/date/StringUtils.java",
+    "java/src/org/chromium/components/browser_ui/util/motion/MotionEventInfo.java",
   ]
 
   deps = [
diff --git a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/OnPeripheralClickListener.java b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/OnPeripheralClickListener.java
index a0cd3b6..b187a2f 100644
--- a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/OnPeripheralClickListener.java
+++ b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/OnPeripheralClickListener.java
@@ -10,6 +10,7 @@
 import android.view.View;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.ui.util.MotionEventUtils;
 
 /**
@@ -35,7 +36,7 @@
 
                             @Override
                             public boolean onSingleTapUp(MotionEvent e) {
-                                onPeripheralClickRunnable.run(e);
+                                onPeripheralClickRunnable.run(MotionEventInfo.fromMotionEvent(e));
                                 return true;
                             }
                         });
@@ -57,8 +58,8 @@
         /**
          * Called when a peripheral click is detected.
          *
-         * @param triggeringMotionEvent {@link MotionEvent} that triggered the click.
+         * @param triggeringMotion {@link MotionEventInfo} that triggered the click.
          */
-        void run(MotionEvent triggeringMotionEvent);
+        void run(MotionEventInfo triggeringMotion);
     }
 }
diff --git a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/OnPeripheralClickListenerTest.java b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/OnPeripheralClickListenerTest.java
index 4eeaafe..8573244 100644
--- a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/OnPeripheralClickListenerTest.java
+++ b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/OnPeripheralClickListenerTest.java
@@ -31,6 +31,7 @@
 import org.chromium.base.test.util.Batch;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.components.browser_ui.util.OnPeripheralClickListener.OnPeripheralClickRunnable;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.ui.test.util.BlankUiTestActivity;
 
 @NullMarked
@@ -185,7 +186,7 @@
         int mNumTimesRun;
 
         @Override
-        public void run(MotionEvent triggeringMotionEvent) {
+        public void run(MotionEventInfo triggeringMotion) {
             mNumTimesRun++;
         }
     }
diff --git a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/motion/MotionEventInfo.java b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/motion/MotionEventInfo.java
new file mode 100644
index 0000000..5999a9e
--- /dev/null
+++ b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/motion/MotionEventInfo.java
@@ -0,0 +1,53 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.browser_ui.util.motion;
+
+import android.view.MotionEvent;
+
+import org.chromium.build.annotations.NullMarked;
+
+/**
+ * A plain old data class that holds information about a {@link MotionEvent}.
+ *
+ * <p>It's safer to use this class when passing information about a {@link MotionEvent}, since using
+ * raw {@link MotionEvent}s needs to ensure they are not recycled by the Android framework before
+ * they are read.
+ *
+ * @see MotionEvent#recycle()
+ */
+@NullMarked
+public final class MotionEventInfo {
+
+    /**
+     * @see MotionEvent#getAction()
+     */
+    public final int action;
+
+    /**
+     * @see MotionEvent#getSource()
+     */
+    public final int source;
+
+    /**
+     * @see MotionEvent#getToolType(int)
+     */
+    public final int[] toolType;
+
+    /** Derives {@link MotionEventInfo} from a {@link MotionEvent}. */
+    public static MotionEventInfo fromMotionEvent(MotionEvent motionEvent) {
+        int[] toolType = new int[motionEvent.getPointerCount()];
+        for (int i = 0; i < toolType.length; i++) {
+            toolType[i] = motionEvent.getToolType(i);
+        }
+
+        return new MotionEventInfo(motionEvent.getAction(), motionEvent.getSource(), toolType);
+    }
+
+    private MotionEventInfo(int action, int source, int[] toolType) {
+        this.action = action;
+        this.source = source;
+        this.toolType = toolType;
+    }
+}
diff --git a/components/browser_ui/widget/android/BUILD.gn b/components/browser_ui/widget/android/BUILD.gn
index 1983e9d..e20b656 100644
--- a/components/browser_ui/widget/android/BUILD.gn
+++ b/components/browser_ui/widget/android/BUILD.gn
@@ -345,6 +345,7 @@
     ":java",
     "//base:base_java_test_support",
     "//base:tasks_java",
+    "//components/browser_ui/util/android:java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/hamcrest:hamcrest_core_java",
@@ -393,6 +394,7 @@
     "//chrome/test/android:chrome_java_test_support_common",
     "//components/browser_ui/test/android:test_support_java",
     "//components/browser_ui/theme/android:java_resources",
+    "//components/browser_ui/util/android:java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:org_jspecify_jspecify_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MenuOrKeyboardActionController.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MenuOrKeyboardActionController.java
index d6f8871..cdb7536b 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MenuOrKeyboardActionController.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MenuOrKeyboardActionController.java
@@ -8,6 +8,7 @@
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 
 /**
  * A controller to register/unregister {@link MenuOrKeyboardActionHandler} for menu or keyboard
@@ -43,10 +44,10 @@
      * <p>The default implementation works for most cases, so it's not recommended to override it
      * unless you are sure.
      *
-     * @see #onMenuOrKeyboardAction(int, boolean, MotionEvent)
+     * @see #onMenuOrKeyboardAction(int, boolean, MotionEventInfo)
      */
     default boolean onMenuOrKeyboardAction(int id, boolean fromMenu) {
-        return onMenuOrKeyboardAction(id, fromMenu, /* triggeringMotionEvent= */ null);
+        return onMenuOrKeyboardAction(id, fromMenu, /* triggeringMotion= */ null);
     }
 
     /**
@@ -55,11 +56,11 @@
      * @param id The ID of the selected menu item (defined in main_menu.xml) or keyboard shortcut
      *     (defined in values.xml).
      * @param fromMenu Whether this was triggered from the menu.
-     * @param triggeringMotionEvent The {@link MotionEvent} that triggered the action; it is {@code
+     * @param triggeringMotion The {@link MotionEventInfo} that triggered the action; it is {@code
      *     null} if {@link MotionEvent} wasn't available when the action was detected, such as in
      *     {@link android.view.View.OnClickListener}.
      * @return Whether the action was handled.
      */
     boolean onMenuOrKeyboardAction(
-            int id, boolean fromMenu, @Nullable MotionEvent triggeringMotionEvent);
+            int id, boolean fromMenu, @Nullable MotionEventInfo triggeringMotion);
 }
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/ListViewTouchTracker.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/ListViewTouchTracker.java
index 567cb55..16d7ae2 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/ListViewTouchTracker.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/ListViewTouchTracker.java
@@ -4,62 +4,17 @@
 
 package org.chromium.components.browser_ui.widget.list_view;
 
-import android.view.MotionEvent;
-
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 
 /** Tracks touch events on a {@link android.widget.ListView}. */
 @NullMarked
 public interface ListViewTouchTracker {
 
     /**
-     * A plain old data class that holds information about a touch {@link MotionEvent}.
-     *
-     * <p>This class should be used when information about a {@link MotionEvent} needs to be kept as
-     * a state. We shouldn't keep {@link MotionEvent}s as states since the Android framework will
-     * recycle them.
-     *
-     * @see MotionEvent#recycle()
-     */
-    final class ListViewTouchInfo {
-
-        /**
-         * @see MotionEvent#getAction()
-         */
-        public final int action;
-
-        /**
-         * @see MotionEvent#getSource()
-         */
-        public final int source;
-
-        /**
-         * @see MotionEvent#getToolType(int)
-         */
-        public final int[] toolType;
-
-        /** Derives {@link ListViewTouchInfo} from a {@link MotionEvent}. */
-        public static ListViewTouchInfo fromMotionEvent(MotionEvent motionEvent) {
-            int[] toolType = new int[motionEvent.getPointerCount()];
-            for (int i = 0; i < toolType.length; i++) {
-                toolType[i] = motionEvent.getToolType(i);
-            }
-
-            return new ListViewTouchInfo(
-                    motionEvent.getAction(), motionEvent.getSource(), toolType);
-        }
-
-        private ListViewTouchInfo(int action, int source, int[] toolType) {
-            this.action = action;
-            this.source = source;
-            this.toolType = toolType;
-        }
-    }
-
-    /**
-     * Returns the last {@link ListViewTouchInfo} that was a single tap up, as detected by {@link
+     * Returns the last {@link MotionEventInfo} that was a single tap up, as detected by {@link
      * android.view.GestureDetector}.
      */
-    @Nullable ListViewTouchInfo getLastSingleTapUp();
+    @Nullable MotionEventInfo getLastSingleTapUp();
 }
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/TouchTrackingListView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/TouchTrackingListView.java
index d08c10c..889db4e 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/TouchTrackingListView.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/TouchTrackingListView.java
@@ -12,6 +12,7 @@
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 
 /**
  * A custom {@link ListView} that tracks touch events.
@@ -52,12 +53,12 @@
             new GestureDetector.SimpleOnGestureListener() {
                 @Override
                 public boolean onSingleTapUp(MotionEvent e) {
-                    mLastSingleTapUpInfo = ListViewTouchInfo.fromMotionEvent(e);
+                    mLastSingleTapUpInfo = MotionEventInfo.fromMotionEvent(e);
                     return true;
                 }
             };
 
-    private @Nullable ListViewTouchInfo mLastSingleTapUpInfo;
+    private @Nullable MotionEventInfo mLastSingleTapUpInfo;
 
     public TouchTrackingListView(Context context) {
         this(context, /* attrs= */ null);
@@ -84,7 +85,7 @@
     }
 
     @Override
-    public @Nullable ListViewTouchInfo getLastSingleTapUp() {
+    public @Nullable MotionEventInfo getLastSingleTapUp() {
         return mLastSingleTapUpInfo;
     }
 }
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/TouchTrackingListViewTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/TouchTrackingListViewTest.java
index f9cf845..570300f 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/TouchTrackingListViewTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/list_view/TouchTrackingListViewTest.java
@@ -32,7 +32,7 @@
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Batch;
-import org.chromium.components.browser_ui.widget.list_view.ListViewTouchTracker.ListViewTouchInfo;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 import org.chromium.components.browser_ui.widget.test.R;
 import org.chromium.ui.test.util.BlankUiTestActivity;
 
@@ -79,7 +79,7 @@
                                     MotionEvent.TOOL_TYPE_MOUSE));
 
                     // Assert
-                    ListViewTouchInfo touchInfo = touchTrackingListView.getLastSingleTapUp();
+                    MotionEventInfo touchInfo = touchTrackingListView.getLastSingleTapUp();
                     assertNotNull(touchInfo);
                     assertEquals(MotionEvent.ACTION_UP, touchInfo.action);
                     assertEquals(InputDevice.SOURCE_MOUSE, touchInfo.source);
diff --git a/components/browser_ui/widget/android/test/java/src/org/chromium/components/browser_ui/widget/list_view/FakeListViewTouchTracker.java b/components/browser_ui/widget/android/test/java/src/org/chromium/components/browser_ui/widget/list_view/FakeListViewTouchTracker.java
index bdfcba3..d4b9443 100644
--- a/components/browser_ui/widget/android/test/java/src/org/chromium/components/browser_ui/widget/list_view/FakeListViewTouchTracker.java
+++ b/components/browser_ui/widget/android/test/java/src/org/chromium/components/browser_ui/widget/list_view/FakeListViewTouchTracker.java
@@ -6,19 +6,20 @@
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.components.browser_ui.util.motion.MotionEventInfo;
 
 /** Fake implementation of {@link ListViewTouchTracker} for testing. */
 @NullMarked
 public final class FakeListViewTouchTracker implements ListViewTouchTracker {
 
-    private @Nullable ListViewTouchInfo mLastSingleTapUpInfo;
+    private @Nullable MotionEventInfo mLastSingleTapUpInfo;
 
     @Override
-    public @Nullable ListViewTouchInfo getLastSingleTapUp() {
+    public @Nullable MotionEventInfo getLastSingleTapUp() {
         return mLastSingleTapUpInfo;
     }
 
-    public void setLastSingleTapUpInfo(@Nullable ListViewTouchInfo listViewTouchInfo) {
+    public void setLastSingleTapUpInfo(@Nullable MotionEventInfo listViewTouchInfo) {
         mLastSingleTapUpInfo = listViewTouchInfo;
     }
 }
diff --git a/components/collaboration/internal/collaboration_service_impl.cc b/components/collaboration/internal/collaboration_service_impl.cc
index 033f99a..4faa78ee 100644
--- a/components/collaboration/internal/collaboration_service_impl.cc
+++ b/components/collaboration/internal/collaboration_service_impl.cc
@@ -106,9 +106,6 @@
     token = parse_result.value();
   }
 
-  // TODO(crbug.com/393194653): Promote the active screen instead of closing and
-  // starting a new flow if flow is ongoing.
-
   CancelAllFlows(base::BindOnce(
       &CollaborationServiceImpl::StartJoinFlowInternal,
       weak_ptr_factory_.GetWeakPtr(), std::move(delegate), token));
@@ -124,11 +121,6 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   metrics::RecordShareOrManageEntryPoint(data_sharing_service_->GetLogger(),
                                          entry);
-  auto it = collaboration_controllers_.find(either_id);
-  if (it != collaboration_controllers_.end()) {
-    it->second->delegate()->PromoteCurrentScreen();
-    return;
-  }
 
   CancelAllFlows(
       base::BindOnce(&CollaborationServiceImpl::StartCollaborationFlowInternal,
@@ -146,11 +138,6 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   metrics::RecordLeaveOrDeleteEntryPoint(data_sharing_service_->GetLogger(),
                                          entry);
-  auto it = collaboration_controllers_.find(either_id);
-  if (it != collaboration_controllers_.end()) {
-    it->second->delegate()->PromoteCurrentScreen();
-    return;
-  }
 
   CancelAllFlows(
       base::BindOnce(&CollaborationServiceImpl::StartCollaborationFlowInternal,
diff --git a/components/commerce/core/subscriptions/subscriptions_server_proxy.h b/components/commerce/core/subscriptions/subscriptions_server_proxy.h
index 4b60238..4e7dd8b 100644
--- a/components/commerce/core/subscriptions/subscriptions_server_proxy.h
+++ b/components/commerce/core/subscriptions/subscriptions_server_proxy.h
@@ -5,7 +5,6 @@
 #ifndef COMPONENTS_COMMERCE_CORE_SUBSCRIPTIONS_SUBSCRIPTIONS_SERVER_PROXY_H_
 #define COMPONENTS_COMMERCE_CORE_SUBSCRIPTIONS_SUBSCRIPTIONS_SERVER_PROXY_H_
 
-#include <queue>
 #include <string>
 #include <unordered_map>
 
diff --git a/components/commerce/core/subscriptions/subscriptions_storage.h b/components/commerce/core/subscriptions/subscriptions_storage.h
index 4224f88d..99737a45 100644
--- a/components/commerce/core/subscriptions/subscriptions_storage.h
+++ b/components/commerce/core/subscriptions/subscriptions_storage.h
@@ -5,7 +5,6 @@
 #ifndef COMPONENTS_COMMERCE_CORE_SUBSCRIPTIONS_SUBSCRIPTIONS_STORAGE_H_
 #define COMPONENTS_COMMERCE_CORE_SUBSCRIPTIONS_SUBSCRIPTIONS_STORAGE_H_
 
-#include <queue>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
diff --git a/components/google/OWNERS b/components/google/OWNERS
deleted file mode 100644
index 340c579..0000000
--- a/components/google/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-isherman@chromium.org
diff --git a/components/history/core/browser/features.cc b/components/history/core/browser/features.cc
index 057f817b..28ce0fc 100644
--- a/components/history/core/browser/features.cc
+++ b/components/history/core/browser/features.cc
@@ -84,4 +84,9 @@
              is_android ? base::FEATURE_ENABLED_BY_DEFAULT
                         : base::FEATURE_DISABLED_BY_DEFAULT);
 
+// If enabled, heuristically remove possible visual duplicates from top sites.
+BASE_FEATURE(kMostVisitedTilesVisualDeduplication,
+             "MostVisitedTilesVisualDeduplication",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 }  // namespace history
diff --git a/components/history/core/browser/features.h b/components/history/core/browser/features.h
index d06240e..72ca2b3c 100644
--- a/components/history/core/browser/features.h
+++ b/components/history/core/browser/features.h
@@ -31,6 +31,9 @@
 // Most Visited Tiles scoring function changes.
 BASE_DECLARE_FEATURE(kMostVisitedTilesNewScoring);
 
+// Most Visited Tiles Visual Deduplication.
+BASE_DECLARE_FEATURE(kMostVisitedTilesVisualDeduplication);
+
 // List of values for |kMvtScoringParamRecencyFactor|
 inline constexpr char kMvtScoringParamRecencyFactor_Classic[] = "default";
 inline constexpr char kMvtScoringParamRecencyFactor_Decay[] = "decay";
diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h
index 66e70da..3a66c8b 100644
--- a/components/infobars/core/infobar_delegate.h
+++ b/components/infobars/core/infobar_delegate.h
@@ -162,7 +162,7 @@
     WEBOTP_SERVICE_INFOBAR_DELEGATE = 95,
     KNOWN_INTERCEPTION_DISCLOSURE_INFOBAR_DELEGATE = 96,
     // Removed: SYNC_ERROR_INFOBAR_DELEGATE_ANDROID = 97,
-    INSECURE_DOWNLOAD_INFOBAR_DELEGATE_ANDROID = 98,
+    // Removed: INSECURE_DOWNLOAD_INFOBAR_DELEGATE_ANDROID = 98,
     // Removed: CONDITIONAL_TAB_STRIP_INFOBAR_ANDROID = 99,
     // Removed: LITE_MODE_HTTPS_IMAGE_COMPRESSION_INFOBAR_ANDROID = 100,
     // Removed: SYSTEM_INFOBAR_DELEGATE_MAC = 101,
diff --git a/components/input/input_router_impl.h b/components/input/input_router_impl.h
index 5d573e8..6165488 100644
--- a/components/input/input_router_impl.h
+++ b/components/input/input_router_impl.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include <memory>
-#include <queue>
 
 #include "base/containers/flat_map.h"
 #include "base/gtest_prod_util.h"
diff --git a/components/media_router/common/providers/cast/channel/cast_socket.h b/components/media_router/common/providers/cast/channel/cast_socket.h
index 68d67f8d..81325de 100644
--- a/components/media_router/common/providers/cast/channel/cast_socket.h
+++ b/components/media_router/common/providers/cast/channel/cast_socket.h
@@ -7,8 +7,6 @@
 
 #include <stdint.h>
 
-#include <queue>
-
 #include "base/cancelable_callback.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
diff --git a/components/metrics/structured/OWNERS b/components/metrics/structured/OWNERS
index 64a308b..6ece1121 100644
--- a/components/metrics/structured/OWNERS
+++ b/components/metrics/structured/OWNERS
@@ -1,5 +1,5 @@
 file://base/metrics/OWNERS
-jongahn@chromium.org
+troywang@google.com
 
 # Last resort reviewers
 tby@chromium.org
diff --git a/components/omnibox/browser/enterprise_search_aggregator_provider.cc b/components/omnibox/browser/enterprise_search_aggregator_provider.cc
index ed08a2b..732431f 100644
--- a/components/omnibox/browser/enterprise_search_aggregator_provider.cc
+++ b/components/omnibox/browser/enterprise_search_aggregator_provider.cc
@@ -220,10 +220,26 @@
     {"video/quicktime", "Quicktime Video"},
 });
 
-// Helper for converting a `mime_type` into an abbreviated string.
-std::string_view MimeToDescription(const std::string_view& mime_type) {
-  const auto it = kMimeTypeMapping.find(mime_type);
-  return it != kMimeTypeMapping.end() ? it->second : "";
+// A mapping from `source_type` to the human readable `content_type_description`.
+const auto kSourceTypeMapping = base::MakeFixedFlatMap<std::string_view,
+                                                       std::string_view>({
+    {"buganizer", "Buganizer Issue"},
+    {"jira", "Jira Issue"},
+    {"salesforce", "Salesforce"},
+    {"slack", "Slack Message"},
+});
+
+// Helper for converting `mime_type` and `source_type` into a human readable
+// string. Prioritizes `mime_type` over `source_type`.
+std::string_view ContentTypeToDescription(const std::string_view& mime_type,
+                                          const std::string_view& source_type) {
+  const auto mimeTypeIter = kMimeTypeMapping.find(mime_type);
+  if (mimeTypeIter != kMimeTypeMapping.end()) {
+    return mimeTypeIter->second;
+  }
+  const auto sourceTypeIter = kSourceTypeMapping.find(source_type);
+  return sourceTypeIter != kSourceTypeMapping.end() ? sourceTypeIter->second
+                                                    : "";
 }
 
 // Helper for converting unix timestamp `time` into an abbreviated date.
@@ -911,13 +927,19 @@
     std::optional<int> response_time =
         result.FindIntByDottedPath("document.derivedStructData.updated_time");
     const std::u16string last_updated = UpdateTimeToString(response_time);
+
     const std::u16string owner = base::UTF8ToUTF16(ptr_to_string(
         result.FindStringByDottedPath("document.derivedStructData.owner")));
-    const std::u16string file_type_description = base::UTF8ToUTF16(
-        MimeToDescription(ptr_to_string(result.FindStringByDottedPath(
-            "document.derivedStructData.mime_type"))));
+
+    const std::u16string content_type_description = base::UTF8ToUTF16(
+        ContentTypeToDescription(
+            ptr_to_string(result.FindStringByDottedPath(
+                "document.derivedStructData.mime_type")),
+            ptr_to_string(result.FindStringByDottedPath(
+                "document.derivedStructData.source_type"))));
+
     return base::UTF16ToUTF8(GetLocalizedContentMetadata(
-        last_updated, owner, file_type_description));
+        last_updated, owner, content_type_description));
   }
 
   return "";
@@ -926,31 +948,31 @@
 std::u16string EnterpriseSearchAggregatorProvider::GetLocalizedContentMetadata(
     const std::u16string& update_time,
     const std::u16string& owner,
-    const std::u16string& file_type_description) const {
+    const std::u16string& content_type_description) const {
   if (!update_time.empty()) {
     if (!owner.empty()) {
-      return !file_type_description.empty()
+      return !content_type_description.empty()
                  ? l10n_util::GetStringFUTF16(
                        IDS_CONTENT_SUGGESTION_DESCRIPTION_TEMPLATE, update_time,
-                       owner, file_type_description)
+                       owner, content_type_description)
                  : l10n_util::GetStringFUTF16(
                        IDS_CONTENT_SUGGESTION_DESCRIPTION_TEMPLATE_WITHOUT_FILE_TYPE_DESCRIPTION,
                        update_time, owner);
     }
-    return !file_type_description.empty()
+    return !content_type_description.empty()
                ? l10n_util::GetStringFUTF16(
                      IDS_CONTENT_SUGGESTION_DESCRIPTION_TEMPLATE_WITHOUT_OWNER,
-                     update_time, file_type_description)
+                     update_time, content_type_description)
                : update_time;
   }
   if (!owner.empty()) {
-    return !file_type_description.empty()
+    return !content_type_description.empty()
                ? l10n_util::GetStringFUTF16(
                      IDS_CONTENT_SUGGESTION_DESCRIPTION_TEMPLATE_WITHOUT_DATE,
-                     owner, file_type_description)
+                     owner, content_type_description)
                : owner;
   }
-  return !file_type_description.empty() ? file_type_description : u"";
+  return !content_type_description.empty() ? content_type_description : u"";
 }
 
 std::vector<std::string>
diff --git a/components/omnibox/browser/enterprise_search_aggregator_provider.h b/components/omnibox/browser/enterprise_search_aggregator_provider.h
index fa920e7..78f4a8d0 100644
--- a/components/omnibox/browser/enterprise_search_aggregator_provider.h
+++ b/components/omnibox/browser/enterprise_search_aggregator_provider.h
@@ -163,11 +163,11 @@
                                SuggestionType suggestion_type) const;
 
   // Helper method to get a localized metadata string depending on which of
-  // `update_time`, `owner`, and `file_type_description` exist.
+  // `update_time`, `owner`, and `content_type_description` exist.
   std::u16string GetLocalizedContentMetadata(
       const std::u16string& update_time,
       const std::u16string& owner,
-      const std::u16string& file_type_description) const;
+      const std::u16string& content_type_description) const;
 
   // Helper method to get user-readable (e.g. 'chromium is awesome
   // document') fields that can be used to compare input similarity.
diff --git a/components/omnibox/browser/enterprise_search_aggregator_provider_unittest.cc b/components/omnibox/browser/enterprise_search_aggregator_provider_unittest.cc
index e00bd08..984ec18 100644
--- a/components/omnibox/browser/enterprise_search_aggregator_provider_unittest.cc
+++ b/components/omnibox/browser/enterprise_search_aggregator_provider_unittest.cc
@@ -389,7 +389,6 @@
       userName, displayName, givenName, familyName, score);
 }
 std::string CreateContentResult(const std::string& title,
-                                const std::string& owner_email,
                                 const std::string& url,
                                 const float score = 0.0) {
   return base::StringPrintf(
@@ -397,15 +396,51 @@
         {
           "document": {
             "derivedStructData": {
+              "title": "%s"
+            }
+          },
+          "destinationUri": "%s",
+          "score": %0.1f
+        }
+        )",
+      title, url, score);
+}
+
+std::string CreateContentResultWithOwnerEmail(const std::string& title,
+                                              const std::string& owner_email,
+                                              const std::string& url) {
+  return base::StringPrintf(
+      R"(
+        {
+          "document": {
+            "derivedStructData": {
               "title": "%s",
               "owner_email": "%s"
             }
           },
-          "destinationUri": "%s",
-          "score": %0.1f
+          "destinationUri": "%s"
         }
         )",
-      title, owner_email, url, score);
+      title, owner_email, url);
+}
+std::string CreateContentResultWithTypes(const std::string& title,
+                                         const std::string& url,
+                                         const std::string& mime_type,
+                                         const std::string& source_type) {
+  return base::StringPrintf(
+      R"(
+        {
+          "document": {
+            "derivedStructData": {
+              "title": "%s",
+              "mime_type": "%s",
+              "source_type": "%s"
+            }
+          },
+          "destinationUri": "%s"
+        }
+        )",
+      title, mime_type, source_type, url);
 }
 std::string CreateResponse(std::vector<std::string> queries,
                            std::vector<std::string> peoples,
@@ -1083,18 +1118,12 @@
                              "familyName"),
       },
       {
-          CreateContentResult("grape-1-content", "mime_type",
-                              "https://url-grape-1/"),
-          CreateContentResult("grape-2-content", "mime_type",
-                              "https://url-grape-2/"),
-          CreateContentResult("grape-3-content", "mime_type",
-                              "https://url-grape-3/"),
-          CreateContentResult("mango-1-content", "mime_type",
-                              "https://url-mango-1/"),
-          CreateContentResult("mango-2-content", "mime_type",
-                              "https://url-mango-2/"),
-          CreateContentResult("mango-3-content", "mime_type",
-                              "https://url-mango-3/"),
+          CreateContentResult("grape-1-content", "https://url-grape-1/"),
+          CreateContentResult("grape-2-content", "https://url-grape-2/"),
+          CreateContentResult("grape-3-content", "https://url-grape-3/"),
+          CreateContentResult("mango-1-content", "https://url-mango-1/"),
+          CreateContentResult("mango-2-content", "https://url-mango-2/"),
+          CreateContentResult("mango-3-content", "https://url-mango-3/"),
       }));
   EXPECT_THAT(
       GetScoredMatches(),
@@ -1139,22 +1168,14 @@
                              "familyName"),
       },
       {
-          CreateContentResult("grape-1-content", "mime_type",
-                              "https://url-grape-1/"),
-          CreateContentResult("grape-2-content", "mime_type",
-                              "https://url-grape-2/"),
-          CreateContentResult("grape-3-content", "mime_type",
-                              "https://url-grape-3/"),
-          CreateContentResult("mango-1-content", "mime_type",
-                              "https://url-mango-1/"),
-          CreateContentResult("mango-2-content", "mime_type",
-                              "https://url-mango-2/"),
-          CreateContentResult("mango-3-content", "mime_type",
-                              "https://url-mango-3/"),
-          CreateContentResult("mango-4-content", "mime_type",
-                              "https://url-mango-4/"),
-          CreateContentResult("mango-5-content", "mime_type",
-                              "https://url-mango-5/"),
+          CreateContentResult("grape-1-content", "https://url-grape-1/"),
+          CreateContentResult("grape-2-content", "https://url-grape-2/"),
+          CreateContentResult("grape-3-content", "https://url-grape-3/"),
+          CreateContentResult("mango-1-content", "https://url-mango-1/"),
+          CreateContentResult("mango-2-content", "https://url-mango-2/"),
+          CreateContentResult("mango-3-content", "https://url-mango-3/"),
+          CreateContentResult("mango-4-content", "https://url-mango-4/"),
+          CreateContentResult("mango-5-content", "https://url-mango-5/"),
       }));
   EXPECT_THAT(
       GetScoredMatches(),
@@ -1174,22 +1195,16 @@
 
   // Types that have less than 2 results aren't backfilled by other types.
   provider_->adjusted_input_ = CreateInput(u"mango m", false);
-  ParseResponse(
-      CreateResponse({}, {},
-                     {
-                         CreateContentResult("grape-1-content", "mime_type",
-                                             "https://url-grape-1/"),
-                         CreateContentResult("grape-2-content", "mime_type",
-                                             "https://url-grape-2/"),
-                         CreateContentResult("grape-3-content", "mime_type",
-                                             "https://url-grape-3/"),
-                         CreateContentResult("mango-1-content", "mime_type",
-                                             "https://url-mango-1/"),
-                         CreateContentResult("mango-2-content", "mime_type",
-                                             "https://url-mango-2/"),
-                         CreateContentResult("mango-3-content", "mime_type",
-                                             "https://url-mango-3/"),
-                     }));
+  ParseResponse(CreateResponse(
+      {}, {},
+      {
+          CreateContentResult("grape-1-content", "https://url-grape-1/"),
+          CreateContentResult("grape-2-content", "https://url-grape-2/"),
+          CreateContentResult("grape-3-content", "https://url-grape-3/"),
+          CreateContentResult("mango-1-content", "https://url-mango-1/"),
+          CreateContentResult("mango-2-content", "https://url-mango-2/"),
+          CreateContentResult("mango-3-content", "https://url-mango-3/"),
+      }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url-mango-1/", 517},
                                    ScoredMatch{u"https://url-mango-2/", 516}));
@@ -1197,16 +1212,13 @@
   // The best 2 suggestions should be shown, even if they're not
   // the 1st 2.
   provider_->adjusted_input_ = CreateInput(u"mango mango-2 mango-3", false);
-  ParseResponse(
-      CreateResponse({}, {},
-                     {
-                         CreateContentResult("mango-1-content", "mime_type",
-                                             "https://url-mango-1/"),
-                         CreateContentResult("mango-2-content", "mime_type",
-                                             "https://url-mango-2/"),
-                         CreateContentResult("mango-3-content", "mime_type",
-                                             "https://url-mango-3/"),
-                     }));
+  ParseResponse(CreateResponse(
+      {}, {},
+      {
+          CreateContentResult("mango-1-content", "https://url-mango-1/"),
+          CreateContentResult("mango-2-content", "https://url-mango-2/"),
+          CreateContentResult("mango-3-content", "https://url-mango-3/"),
+      }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url-mango-2/", 519},
                                    ScoredMatch{u"https://url-mango-3/", 518}));
@@ -1237,18 +1249,12 @@
                              "familyName"),
       },
       {
-          CreateContentResult("grape-1-content", "mime_type",
-                              "https://url-grape-1/"),
-          CreateContentResult("grape-2-content", "mime_type",
-                              "https://url-grape-2/"),
-          CreateContentResult("grape-3-content", "mime_type",
-                              "https://url-grape-3/"),
-          CreateContentResult("mango-1-content", "mime_type",
-                              "https://url-mango-1/"),
-          CreateContentResult("mango-2-content", "mime_type",
-                              "https://url-mango-2/"),
-          CreateContentResult("mango-3-content", "mime_type",
-                              "https://url-mango-3/"),
+          CreateContentResult("grape-1-content", "https://url-grape-1/"),
+          CreateContentResult("grape-2-content", "https://url-grape-2/"),
+          CreateContentResult("grape-3-content", "https://url-grape-3/"),
+          CreateContentResult("mango-1-content", "https://url-mango-1/"),
+          CreateContentResult("mango-2-content", "https://url-mango-2/"),
+          CreateContentResult("mango-3-content", "https://url-mango-3/"),
       }));
   EXPECT_THAT(
       GetScoredMatches(),
@@ -1291,18 +1297,12 @@
                              "familyName"),
       },
       {
-          CreateContentResult("grape-1-content", "mime_type",
-                              "https://url-grape-1/"),
-          CreateContentResult("grape-2-content", "mime_type",
-                              "https://url-grape-2/"),
-          CreateContentResult("grape-3-content", "mime_type",
-                              "https://url-grape-3/"),
-          CreateContentResult("mango-1-content", "mime_type",
-                              "https://url-mango-1/"),
-          CreateContentResult("mango-2-content", "mime_type",
-                              "https://url-mango-2/"),
-          CreateContentResult("mango-3-content", "mime_type",
-                              "https://url-mango-3/"),
+          CreateContentResult("grape-1-content", "https://url-grape-1/"),
+          CreateContentResult("grape-2-content", "https://url-grape-2/"),
+          CreateContentResult("grape-3-content", "https://url-grape-3/"),
+          CreateContentResult("mango-1-content", "https://url-mango-1/"),
+          CreateContentResult("mango-2-content", "https://url-mango-2/"),
+          CreateContentResult("mango-3-content", "https://url-mango-3/"),
       }));
   EXPECT_THAT(
       GetScoredMatches(),
@@ -1338,18 +1338,12 @@
                              "familyName"),
       },
       {
-          CreateContentResult("grape-1-content", "mime_type",
-                              "https://url-grape-1/"),
-          CreateContentResult("grape-2-content", "mime_type",
-                              "https://url-grape-2/"),
-          CreateContentResult("grape-3-content", "mime_type",
-                              "https://url-grape-3/"),
-          CreateContentResult("mango-1-content", "mime_type",
-                              "https://url-mango-1/"),
-          CreateContentResult("mango-2-content", "mime_type",
-                              "https://url-mango-2/"),
-          CreateContentResult("mango-3-content", "mime_type",
-                              "https://url-mango-3/"),
+          CreateContentResult("grape-1-content", "https://url-grape-1/"),
+          CreateContentResult("grape-2-content", "https://url-grape-2/"),
+          CreateContentResult("grape-3-content", "https://url-grape-3/"),
+          CreateContentResult("mango-1-content", "https://url-mango-1/"),
+          CreateContentResult("mango-2-content", "https://url-mango-2/"),
+          CreateContentResult("mango-3-content", "https://url-mango-3/"),
       }));
   EXPECT_THAT(
       GetScoredMatches(),
@@ -1385,9 +1379,9 @@
                              "familyName", 0.0),
       },
       {
-          CreateContentResult("matchTitle", "xmime_type", "https://url/", 0.7),
-          CreateContentResult("title", "xmime_type", "https://url2/", 0.7),
-          CreateContentResult("title2", "xmime_type", "https://url3/", 0.3),
+          CreateContentResult("matchTitle", "https://url/", 0.7),
+          CreateContentResult("title", "https://url2/", 0.7),
+          CreateContentResult("title2", "https://url3/", 0.3),
       }));
   EXPECT_THAT(
       GetScoredMatches(),
@@ -1416,8 +1410,8 @@
                              "familyName", 0.6),
       },
       {
-          CreateContentResult("matchTitle", "xmime_type", "https://url/", 0.7),
-          CreateContentResult("title2", "xmime_type", "https://url2/", 0.3),
+          CreateContentResult("matchTitle","https://url/", 0.7),
+          CreateContentResult("title2", "https://url2/", 0.3),
       });
 
   // Scoped mode should use server-provided relevance scores.
@@ -1457,8 +1451,8 @@
                              "familyName"),
       },
       {
-          CreateContentResult("title", "xmime_type", "https://url/"),
-          CreateContentResult("matchTitle", "xmime_type", "https://url/"),
+          CreateContentResult("title", "https://url/"),
+          CreateContentResult("matchTitle", "https://url/"),
       }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(
@@ -1472,11 +1466,10 @@
   ParseResponse(CreateResponse(
       {}, {},
       {
-          CreateContentResult("zero", "mime_type", "https://url-0/"),
-          CreateContentResult("zero one", "mime_type", "https://url-01/"),
-          CreateContentResult("zero one two", "mime_type", "https://url-012/"),
-          CreateContentResult("zero one two three", "mime_type",
-                              "https://url-0123/"),
+          CreateContentResult("zero", "https://url-0/"),
+          CreateContentResult("zero one", "https://url-01/"),
+          CreateContentResult("zero one two", "https://url-012/"),
+          CreateContentResult("zero one two three", "https://url-0123/"),
       }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url-0123/", 717},
@@ -1489,72 +1482,72 @@
   // - If the result field repeats a word, only 1 should count.
   // - If a word appears in multiple result fields, only 1 should count.
   provider_->adjusted_input_ = CreateInput(u"one one", true);
-  ParseResponse(CreateResponse(
-      {}, {},
-      {
-          CreateContentResult("one one", "one one", "https://url-1/"),
-      }));
+  ParseResponse(CreateResponse({}, {},
+                               {
+                                   CreateContentResultWithOwnerEmail(
+                                       "one one", "one one", "https://url-1/"),
+                               }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url-1/", 420}));
 
   // Each input word can match only 1 result word.
   provider_->adjusted_input_ = CreateInput(u"one one", true);
-  ParseResponse(CreateResponse(
-      {}, {},
-      {
-          CreateContentResult("one oneTwo", "mime_type", "https://url/"),
-      }));
+  ParseResponse(
+      CreateResponse({}, {},
+                     {
+                         CreateContentResult("one oneTwo", "https://url/"),
+                     }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url/", 420}));
 
   // A result word can match multiple input words. This is just a side effect
   // of the implementation rather than intentional design.
   provider_->adjusted_input_ = CreateInput(u"one on o", true);
-  ParseResponse(CreateResponse(
-      {}, {},
-      {
-          CreateContentResult("one", "mime_type", "https://url/"),
-      }));
+  ParseResponse(CreateResponse({}, {},
+                               {
+                                   CreateContentResult("one", "https://url/"),
+                               }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url/", 620}));
 
   // Matches outside contents and description contribute less to the score.
   provider_->adjusted_input_ = CreateInput(u"one two three four five", true);
-  ParseResponse(
-      CreateResponse({}, {},
-                     {
-                         CreateContentResult("title one", "two three four five",
-                                             "https://inside/"),
-                         CreateContentResult("title", "one two three four five",
-                                             "https://outside/"),
-                     }));
+  ParseResponse(CreateResponse(
+      {}, {},
+      {
+          CreateContentResultWithOwnerEmail("title one", "two three four five",
+                                            "https://inside/"),
+          CreateContentResultWithOwnerEmail("title", "one two three four five",
+                                            "https://outside/"),
+      }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://inside/", 820},
                                    ScoredMatch{u"https://outside/", 519}));
 
   // Short input words contribute less to the score.
   provider_->adjusted_input_ = CreateInput(u"on two three four five", true);
-  ParseResponse(CreateResponse(
-      {}, {},
-      {
-          CreateContentResult("one", "two three four five", "https://url/"),
-      }));
+  ParseResponse(
+      CreateResponse({}, {},
+                     {
+                         CreateContentResultWithOwnerEmail(
+                             "one", "two three four five", "https://url/"),
+                     }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url/", 520}));
 
   // Short input words contribute less to score, except for exact (non-prefix)
   // matches in people suggestions.
   provider_->adjusted_input_ = CreateInput(u"weak ab", true);
-  ParseResponse(
-      CreateResponse({},
-                     {
-                         {CreatePeopleResult("ab", "ab", "weak", "")},
-                         {CreatePeopleResult("abc", "abc", "weak", "")},
-                     },
-                     {
-                         CreateContentResult("ab", "weak", "https://url-ab/"),
-                         CreateContentResult("abc", "weak", "https://url-abc/"),
-                     }));
+  ParseResponse(CreateResponse(
+      {},
+      {
+          {CreatePeopleResult("ab", "ab", "weak", "")},
+          {CreatePeopleResult("abc", "abc", "weak", "")},
+      },
+      {
+          CreateContentResultWithOwnerEmail("ab", "weak", "https://url-ab/"),
+          CreateContentResultWithOwnerEmail("abc", "weak", "https://url-abc/"),
+      }));
   EXPECT_THAT(
       GetScoredMatches(),
       testing::ElementsAre(ScoredMatch{u"https://www.google.com/?q=ab", 610},
@@ -1572,8 +1565,9 @@
           {CreatePeopleResult("abcd", "abcd", "weak", "")},
       },
       {
-          CreateContentResult("abc", "weak", "https://url-abc/"),
-          CreateContentResult("abcd", "weak", "https://url-abcd/"),
+          CreateContentResultWithOwnerEmail("abc", "weak", "https://url-abc/"),
+          CreateContentResultWithOwnerEmail("abcd", "weak",
+                                            "https://url-abcd/"),
       }));
   EXPECT_THAT(
       GetScoredMatches(),
@@ -1582,13 +1576,14 @@
                            ScoredMatch{u"https://url-abc/", 520},
                            ScoredMatch{u"https://url-abcd/", 519}));
 
-  // Matches outside human-readable fields aren't considered in scoring.
+  // Matches outside human-readable fields (e.g. URL) aren't considered in
+  // scoring.
   provider_->adjusted_input_ = CreateInput(u"title url", true);
   ParseResponse(
       CreateResponse({}, {},
                      {
-                         CreateContentResult("title", "mime", "https://url1/"),
-                         CreateContentResult("title", "mime", "https://url2/"),
+                         CreateContentResult("title", "https://url1/"),
+                         CreateContentResult("title", "https://url2/"),
                      }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url1/", 420},
@@ -1597,77 +1592,79 @@
   // Suggestions that match every input words, when there are at least 2, should
   // be scored higher.
   provider_->adjusted_input_ = CreateInput(u"one two", true);
-  ParseResponse(CreateResponse(
-      {}, {},
-      {
-          CreateContentResult("one two three", "mime", "https://url/"),
-      }));
+  ParseResponse(
+      CreateResponse({}, {},
+                     {
+                         CreateContentResult("one two three", "https://url/"),
+                     }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url/", 1020}));
 
   // Suggestions that match every input words, when there is not at least 2,
   // should not be scored higher.
   provider_->adjusted_input_ = CreateInput(u"one", true);
-  ParseResponse(CreateResponse(
-      {}, {},
-      {
-          CreateContentResult("one two three", "mime", "https://url/"),
-      }));
+  ParseResponse(
+      CreateResponse({}, {},
+                     {
+                         CreateContentResult("one two three", "https://url/"),
+                     }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url/", 420}));
 
   // Suggestions that match at least 2 but not all inputs words should not be
   // scored higher.
   provider_->adjusted_input_ = CreateInput(u"one two four", true);
-  ParseResponse(CreateResponse(
-      {}, {},
-      {
-          CreateContentResult("one two three", "mime", "https://url/"),
-      }));
+  ParseResponse(
+      CreateResponse({}, {},
+                     {
+                         CreateContentResult("one two three", "https://url/"),
+                     }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url/", 820}));
 
   // Require at least 1 strong match or 2 weak matches.
   provider_->adjusted_input_ = CreateInput(u"title", true);
-  ParseResponse(
-      CreateResponse({}, {},
-                     {
-                         CreateContentResult("title", "mime", "https://url/"),
-                     }));
+  ParseResponse(CreateResponse({}, {},
+                               {
+                                   CreateContentResult("title", "https://url/"),
+                               }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url/", 420}));
 
   // When unscoped, requires at least 1 strong match or 2 weak matches.
-  provider_->adjusted_input_ = CreateInput(u"mimeA mimeB", false);
-  ParseResponse(CreateResponse(
-      {}, {},
-      {
-          CreateContentResult("title", "mimeA", "https://url-1/"),
-          CreateContentResult("title", "mimeA mimeB", "https://url-2/"),
-      }));
+  provider_->adjusted_input_ = CreateInput(u"user gmail", false);
+  ParseResponse(
+      CreateResponse({}, {},
+                     {
+                         CreateContentResultWithOwnerEmail(
+                             "title", "user@example.com", "https://url-1/"),
+                         CreateContentResultWithOwnerEmail(
+                             "title", "user@gmail.com", "https://url-2/"),
+                     }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url-2/", 219},
                                    FieldsAre(_, 0)));
 
   // When scoped, does not require at least 1 strong match or 2 weak matches.
-  provider_->adjusted_input_ = CreateInput(u"mimeA mimeB", true);
-  ParseResponse(CreateResponse(
-      {}, {},
-      {
-          CreateContentResult("title", "mimeA", "https://url-1/"),
-          CreateContentResult("title", "mimeA mimeB", "https://url-2/"),
-      }));
+  provider_->adjusted_input_ = CreateInput(u"user gmail", true);
+  ParseResponse(
+      CreateResponse({}, {},
+                     {
+                         CreateContentResultWithOwnerEmail(
+                             "title", "user@example.com", "https://url-1/"),
+                         CreateContentResultWithOwnerEmail(
+                             "title", "user@gmail.com", "https://url-2/"),
+                     }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(ScoredMatch{u"https://url-2/", 219},
                                    ScoredMatch{u"https://url-1/", 120}));
 
   // Require at least half the input words to match.
   provider_->adjusted_input_ = CreateInput(u"title x y", true);
-  ParseResponse(
-      CreateResponse({}, {},
-                     {
-                         CreateContentResult("title", "mime", "https://url/"),
-                     }));
+  ParseResponse(CreateResponse({}, {},
+                               {
+                                   CreateContentResult("title", "https://url/"),
+                               }));
   EXPECT_THAT(GetScoredMatches(), testing::ElementsAre(FieldsAre(_, 0)));
 
   // People matches should be boosted.
@@ -1683,7 +1680,7 @@
                              "familyName"),
       },
       {
-          CreateContentResult("title input", "mime_type", "https://url/"),
+          CreateContentResult("title input", "https://url/"),
       }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(
@@ -1705,7 +1702,7 @@
                              "familyName"),
       },
       {
-          CreateContentResult("title query", "mime_type", "https://url/"),
+          CreateContentResult("title query", "https://url/"),
       }));
   EXPECT_THAT(
       GetScoredMatches(),
@@ -1722,7 +1719,7 @@
       },
       {},
       {
-          CreateContentResult("query", "mime_type", "https://url/"),
+          CreateContentResult("query", "https://url/"),
       }));
   EXPECT_THAT(GetScoredMatches(),
               testing::ElementsAre(
@@ -1730,6 +1727,42 @@
                   ScoredMatch{u"https://www.google.com/?q=query", 410}));
 }
 
+TEST_F(EnterpriseSearchAggregatorProviderTest,
+       ContentSuggestionTypeDescriptions) {
+  provider_->adjusted_input_ = CreateInput(u"input", true);
+  ParseResponse(CreateResponse(
+      {}, {},
+      {
+          // Verifies use of MIME type.
+          CreateContentResultWithTypes("Evolution of Dance", "https://url1/",
+                                       "video/quicktime", ""),
+          // Verifies use of source type.
+          CreateContentResultWithTypes("Uh oh", "https://url2/", "",
+                                       "buganizer"),
+          // Verifies that MIME type takes precedent over source type.
+          CreateContentResultWithTypes("Same thing we do every night, Pinky",
+                                       "https://url3/", "image/png",
+                                       "salesforce"),
+      }));
+  ACMatches matches = provider_->matches_;
+  ASSERT_EQ(matches.size(), 3u);
+
+  // Verifies use of MIME type.
+  EXPECT_EQ(matches[0].contents, u"Quicktime Video");
+  EXPECT_EQ(matches[0].description, u"Evolution of Dance");
+  EXPECT_EQ(matches[0].destination_url, GURL("https://url1/"));
+
+  // Verifies use of source type.
+  EXPECT_EQ(matches[1].contents, u"Buganizer Issue");
+  EXPECT_EQ(matches[1].description, u"Uh oh");
+  EXPECT_EQ(matches[1].destination_url, GURL("https://url2/"));
+
+  // Verifies that MIME type takes precedent over source type.
+  EXPECT_EQ(matches[2].contents, u"PNG");
+  EXPECT_EQ(matches[2].description, u"Same thing we do every night, Pinky");
+  EXPECT_EQ(matches[2].destination_url, GURL("https://url3/"));
+}
+
 TEST_F(EnterpriseSearchAggregatorProviderTest, Logging) {
   // The code flow is:
   // 1) `Start()`
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 315d910..e3a04a2 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 315d910d17d14d2132ec008d5867390276e54386
+Subproject commit e3a04a219778f8c35eef841cab715b5afd032e2f
diff --git a/components/page_load_metrics/browser/responsiveness_metrics_normalization.h b/components/page_load_metrics/browser/responsiveness_metrics_normalization.h
index dc1e613..419b3c2 100644
--- a/components/page_load_metrics/browser/responsiveness_metrics_normalization.h
+++ b/components/page_load_metrics/browser/responsiveness_metrics_normalization.h
@@ -5,8 +5,6 @@
 #ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_RESPONSIVENESS_METRICS_NORMALIZATION_H_
 #define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_RESPONSIVENESS_METRICS_NORMALIZATION_H_
 
-#include <queue>
-
 #include "base/time/time.h"
 #include "components/page_load_metrics/common/page_load_metrics.mojom.h"
 #include "third_party/blink/public/common/features.h"
diff --git a/components/passage_embeddings/internal/scheduling_embedder.h b/components/passage_embeddings/internal/scheduling_embedder.h
index 47cd0700..4804f7687 100644
--- a/components/passage_embeddings/internal/scheduling_embedder.h
+++ b/components/passage_embeddings/internal/scheduling_embedder.h
@@ -5,9 +5,9 @@
 #ifndef COMPONENTS_PASSAGE_EMBEDDINGS_INTERNAL_SCHEDULING_EMBEDDER_H_
 #define COMPONENTS_PASSAGE_EMBEDDINGS_INTERNAL_SCHEDULING_EMBEDDER_H_
 
+#include <deque>
 #include <memory>
 #include <optional>
-#include <queue>
 #include <string>
 #include <vector>
 
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java
index d276fba..6b7a66f 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java
@@ -1103,6 +1103,7 @@
      */
     private void sendCanMakePaymentResponseToRenderer(boolean response) {
         if (mClient == null) return;
+        Log.i(TAG, "Can make payment: \"%b\".", response);
         mClient.onCanMakePayment(
                 response
                         ? CanMakePaymentQueryResult.CAN_MAKE_PAYMENT
@@ -1146,13 +1147,16 @@
         int result;
         if (HasEnrolledInstrumentQuery.canQuery(
                 mWebContents, mTopLevelOrigin, mPaymentRequestOrigin, mQueryForQuota)) {
+            Log.i(TAG, "Has enrolled instrument: \"%b\".", response);
             result =
                     response
                             ? HasEnrolledInstrumentQueryResult.HAS_ENROLLED_INSTRUMENT
                             : HasEnrolledInstrumentQueryResult.HAS_NO_ENROLLED_INSTRUMENT;
         } else if (shouldEnforceHasEnrolledInstrumentQueryQuota()) {
+            Log.i(TAG, "Has enrolled instrument: No quota.");
             result = HasEnrolledInstrumentQueryResult.QUERY_QUOTA_EXCEEDED;
         } else {
+            Log.i(TAG, "Has enrolled instrument: \"%b\".", response);
             result =
                     response
                             ? HasEnrolledInstrumentQueryResult.WARNING_HAS_ENROLLED_INSTRUMENT
@@ -1555,6 +1559,7 @@
 
     /** The component part of the {@link PaymentRequest#canMakePayment} implementation. */
     /* package */ void canMakePayment() {
+        Log.i(TAG, "Check can make payment.");
         if (sNativeObserverForTest != null) {
             sNativeObserverForTest.onCanMakePaymentCalled();
         }
@@ -1568,6 +1573,7 @@
 
     /** The component part of the {@link PaymentRequest#hasEnrolledInstrument} implementation. */
     /* package */ void hasEnrolledInstrument() {
+        Log.i(TAG, "Check has enrolled instrument.");
         if (sNativeObserverForTest != null) {
             sNativeObserverForTest.onHasEnrolledInstrumentCalled();
         }
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml
index dd0dcdf..86486d9 100644
--- a/components/policy/resources/templates/policies.yaml
+++ b/components/policy/resources/templates/policies.yaml
@@ -1361,6 +1361,7 @@
   1360: NTPFooterExtensionAttributionEnabled
   1361: ClearWindowNameForNewBrowsingContextGroup
   1362: PasswordManagerBlocklist
+  1363: TLS13EarlyDataEnabled
 
 atomic_groups:
   1: Homepage
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/TLS13EarlyDataEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/TLS13EarlyDataEnabled.yaml
new file mode 100644
index 0000000..d2a985b
--- /dev/null
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/TLS13EarlyDataEnabled.yaml
@@ -0,0 +1,41 @@
+caption: Enable TLS 1.3 Early Data
+default: true
+desc: |-
+  TLS 1.3 Early Data is an extension to TLS 1.3 to send an HTTP request simultaneously with the TLS handshake.
+
+        If this policy is not configured, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will follow the default rollout process for TLS 1.3 Early Data.
+
+        If it is enabled, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will enable TLS 1.3 Early Data.
+
+        If it is disabled, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will not enable TLS 1.3 Early Data.
+
+        When the feature is enabled, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> may or may not use TLS 1.3 Early Data depending on server support.
+
+        TLS 1.3 Early Data is an established protocol. Existing TLS servers, middleboxes, and security software are expected to either handle or reject TLS 1.3 Early Data without dropping the connection.
+
+        However, devices that do not correctly implement TLS may malfunction and disconnect when TLS 1.3 Early Data is in use. If this occurs, administrators should contact the vendor for a fix.
+
+        This policy is a temporary measure to control the feature and will be removed afterwards. The policy may be enabled to allow you to test for issues and disabled while issues are being resolved.
+example_value: true
+features:
+  dynamic_refresh: true
+  per_profile: false
+future_on:
+- fuchsia
+items:
+- caption: Enable the TLS 1.3 Early Data
+  value: true
+- caption: Disable the TLS 1.3 Early Data
+  value: false
+owners:
+- bashi@chromium.org
+- blink-network-stack@google.com
+schema:
+  type: boolean
+supported_on:
+- chrome.*:138-
+- chrome_os:138-
+- android:138-
+tags:
+- system-security
+type: main
diff --git a/components/policy/resources/webui/policy.html b/components/policy/resources/webui/policy.html
index 8103fa73e..6c4c4bd 100644
--- a/components/policy/resources/webui/policy.html
+++ b/components/policy/resources/webui/policy.html
@@ -74,7 +74,7 @@
   <main id="policy-ui-container">
     <div id="policy-ui">
       <section id="status-section" class="status-box-section" hidden>
-        <h3>$i18n{status}</h3>
+        <h2>$i18n{status}</h2>
         <div id="status-box-container"></div>
       </section>
       <section id="main-section" class="empty">
diff --git a/components/policy/resources/webui/policy_table.html b/components/policy/resources/webui/policy_table.html
index 61a5f217..2bfd92b 100644
--- a/components/policy/resources/webui/policy_table.html
+++ b/components/policy/resources/webui/policy_table.html
@@ -139,7 +139,7 @@
 </if>
 </style>
 <div class="policy-table" role="table" aria-labelledby="policy-header">
-  <h3 class="header" id="policy-header"></h3>
+  <h2 class="header" id="policy-header"></h2>
   <p class="id"></p>
   <div class="main">
     <div class="header row" role="row">
diff --git a/components/policy/resources/webui/status_box.html b/components/policy/resources/webui/status_box.html
index d32ae4a2..8523cfb2 100644
--- a/components/policy/resources/webui/status_box.html
+++ b/components/policy/resources/webui/status_box.html
@@ -46,7 +46,7 @@
 }
 </style>
 <div class="status-box-fields">
-  <p class="status-box-heading"></p>
+  <h3 class="status-box-heading"></h3>
   <div class="status-entry" hidden>
     <div class="label">$i18n{labelVersion}</div>
     <div class="version status-entry-value"></div>
diff --git a/components/policy/test/data/pref_mapping/TLS13EarlyDataEnabled.json b/components/policy/test/data/pref_mapping/TLS13EarlyDataEnabled.json
new file mode 100644
index 0000000..4c647b8
--- /dev/null
+++ b/components/policy/test/data/pref_mapping/TLS13EarlyDataEnabled.json
@@ -0,0 +1,21 @@
+[
+  {
+    "os": [
+      "win",
+      "linux",
+      "mac",
+      "chromeos",
+      "android",
+      "fuchsia"
+    ],
+    "simple_policy_pref_mapping_test": {
+      "pref_name": "ssl.tls13_early_data_enabled",
+      "pref_location": "local_state",
+      "default_value": false,
+      "values_to_test": [
+        true,
+        false
+      ]
+    }
+  }
+]
diff --git a/components/privacy_sandbox/tracking_protection_settings.h b/components/privacy_sandbox/tracking_protection_settings.h
index 2ca37f78..627c9a16 100644
--- a/components/privacy_sandbox/tracking_protection_settings.h
+++ b/components/privacy_sandbox/tracking_protection_settings.h
@@ -9,6 +9,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/content_settings/core/common/cookie_controls_state.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/policy/core/common/management/management_service.h"
 #include "components/privacy_sandbox/tracking_protection_prefs.h"
@@ -20,6 +21,11 @@
 
 class TrackingProtectionSettingsObserver;
 
+inline bool IsTrackingProtectionsUi(CookieControlsState controls_state) {
+  return controls_state == CookieControlsState::kActiveTp ||
+         controls_state == CookieControlsState::kPausedTp;
+}
+
 // A service which provides an interface for observing and reading tracking
 // protection settings.
 class TrackingProtectionSettings : public KeyedService {
diff --git a/components/reporting/client/filtered_report_queue.h b/components/reporting/client/filtered_report_queue.h
index 94656ae..382917ac 100644
--- a/components/reporting/client/filtered_report_queue.h
+++ b/components/reporting/client/filtered_report_queue.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_REPORTING_CLIENT_FILTERED_REPORT_QUEUE_H_
 
 #include <memory>
-#include <queue>
 #include <string>
 #include <type_traits>
 #include <utility>
diff --git a/components/reporting/client/report_queue.h b/components/reporting/client/report_queue.h
index 814a44c..5bc5bbb 100644
--- a/components/reporting/client/report_queue.h
+++ b/components/reporting/client/report_queue.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_REPORTING_CLIENT_REPORT_QUEUE_H_
 
 #include <memory>
-#include <queue>
 #include <string>
 #include <utility>
 
diff --git a/components/services/storage/shared_storage/async_shared_storage_database.h b/components/services/storage/shared_storage/async_shared_storage_database.h
index 73b3803..d7a1cf2 100644
--- a/components/services/storage/shared_storage/async_shared_storage_database.h
+++ b/components/services/storage/shared_storage/async_shared_storage_database.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_ASYNC_SHARED_STORAGE_DATABASE_H_
 
 #include <memory>
-#include <queue>
 #include <string>
 #include <vector>
 
diff --git a/components/services/storage/shared_storage/async_shared_storage_database_impl.h b/components/services/storage/shared_storage/async_shared_storage_database_impl.h
index 83d35ea..71f917c 100644
--- a/components/services/storage/shared_storage/async_shared_storage_database_impl.h
+++ b/components/services/storage/shared_storage/async_shared_storage_database_impl.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_ASYNC_SHARED_STORAGE_DATABASE_IMPL_H_
 
 #include <memory>
-#include <queue>
 #include <string>
 #include <vector>
 
diff --git a/components/services/storage/shared_storage/shared_storage_manager.h b/components/services/storage/shared_storage/shared_storage_manager.h
index 3c3ec66..5b13cddf 100644
--- a/components/services/storage/shared_storage/shared_storage_manager.h
+++ b/components/services/storage/shared_storage/shared_storage_manager.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_MANAGER_H_
 
 #include <memory>
-#include <queue>
 #include <vector>
 
 #include "base/files/file_path.h"
diff --git a/components/session_proto_db/session_proto_db.h b/components/session_proto_db/session_proto_db.h
index aeeb5e9..5f5f15c 100644
--- a/components/session_proto_db/session_proto_db.h
+++ b/components/session_proto_db/session_proto_db.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 #include <optional>
-#include <queue>
 #include <string>
 #include <utility>
 #include <vector>
diff --git a/components/spellcheck/renderer/spellcheck_language.h b/components/spellcheck/renderer/spellcheck_language.h
index 9d1eeed..67ee7e1 100644
--- a/components/spellcheck/renderer/spellcheck_language.h
+++ b/components/spellcheck/renderer/spellcheck_language.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_LANGUAGE_H_
 
 #include <memory>
-#include <queue>
 #include <string>
 #include <string_view>
 #include <vector>
diff --git a/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle_unittest.cc b/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle_unittest.cc
index 8bd3dbe0..65d8b00 100644
--- a/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/safe_browsing_page_activation_throttle_unittest.cc
@@ -152,8 +152,7 @@
 }  //  namespace
 
 class SafeBrowsingPageActivationThrottleTest
-    : public content::RenderViewHostTestHarness,
-      public content::WebContentsObserver {
+    : public content::RenderViewHostTestHarness {
  public:
   SafeBrowsingPageActivationThrottleTest()
       : content::RenderViewHostTestHarness(
@@ -189,7 +188,6 @@
         /*database_manager=*/nullptr, ruleset_dealer_.get());
     fake_safe_browsing_database_ = new FakeSafeBrowsingDatabaseManager();
     NavigateAndCommit(GURL("https://test.com"));
-    Observe(contents);
 
     observer_ = std::make_unique<TestSubresourceFilterObserver>(contents);
 
@@ -199,7 +197,12 @@
         &message_dispatcher_bridge_);
 #endif
 
-    SetUpThrottleInserter();
+    throttle_inserter_ =
+        std::make_unique<content::TestNavigationThrottleInserter>(
+            content::RenderViewHostTestHarness::web_contents(),
+            base::BindRepeating(
+                &SafeBrowsingPageActivationThrottleTest::InsertThrottle,
+                base::Unretained(this)));
   }
 
   virtual void Configure() {
@@ -230,27 +233,15 @@
 
   TestSubresourceFilterObserver* observer() { return observer_.get(); }
 
-  // content::WebContentsObserver:
-  void DidStartNavigation(
-      content::NavigationHandle* navigation_handle) override {}
-
-  void SetUpThrottleInserter() {
-    throttle_inserter_ =
-        std::make_unique<content::TestNavigationThrottleInserter>(
-            content::RenderViewHostTestHarness::web_contents(),
-            base::BindLambdaForTesting(
-                [&](content::NavigationThrottleRegistry& registry) -> void {
-                  auto& navigation_handle = registry.GetNavigationHandle();
-                  if (IsInSubresourceFilterRoot(&navigation_handle)) {
-                    registry.AddThrottle(
-                        std::make_unique<SafeBrowsingPageActivationThrottle>(
-                            registry, delegate(),
-                            fake_safe_browsing_database_));
-                  }
-                  ContentSubresourceFilterThrottleManager::FromNavigationHandle(
-                      registry.GetNavigationHandle())
-                      ->MaybeCreateAndAddNavigationThrottles(registry);
-                }));
+  virtual void InsertThrottle(content::NavigationThrottleRegistry& registry) {
+    auto& navigation_handle = registry.GetNavigationHandle();
+    if (IsInSubresourceFilterRoot(&navigation_handle)) {
+      registry.AddThrottle(std::make_unique<SafeBrowsingPageActivationThrottle>(
+          registry, delegate(), fake_safe_browsing_database_));
+    }
+    ContentSubresourceFilterThrottleManager::FromNavigationHandle(
+        registry.GetNavigationHandle())
+        ->MaybeCreateAndAddNavigationThrottles(registry);
   }
 
   // Returns the frame host the navigation committed in, or nullptr if it did
@@ -429,12 +420,11 @@
 
   ~SafeBrowsingPageActivationThrottleTestWithCancelling() override {}
 
-  void DidStartNavigation(content::NavigationHandle* handle) override {
-    auto throttle = std::make_unique<content::TestNavigationThrottle>(handle);
+  void InsertThrottle(content::NavigationThrottleRegistry& registry) override {
+    auto throttle = std::make_unique<content::TestNavigationThrottle>(registry);
     throttle->SetResponse(throttle_method_, result_sync_,
                           content::NavigationThrottle::CANCEL);
-    handle->RegisterThrottleForTesting(std::move(throttle));
-    SafeBrowsingPageActivationThrottleTest::DidStartNavigation(handle);
+    registry.AddThrottle(std::move(throttle));
   }
 
   content::TestNavigationThrottle::ThrottleMethod throttle_method() {
diff --git a/components/tabs/public/tab_strip_collection.h b/components/tabs/public/tab_strip_collection.h
index 0552c8c..4795ffb 100644
--- a/components/tabs/public/tab_strip_collection.h
+++ b/components/tabs/public/tab_strip_collection.h
@@ -105,7 +105,6 @@
                    const std::vector<TabInterface*>& tabs,
                    split_tabs::SplitTabVisualData visual_data);
   void Unsplit(split_tabs::SplitTabId split_id);
-  void MoveSplit(split_tabs::SplitTabId split_id, bool pinned);
   void InsertSplitTabAt(std::unique_ptr<SplitTabCollection> split_collection,
                         int index,
                         int pinned,
diff --git a/components/tabs/tab_strip_collection.cc b/components/tabs/tab_strip_collection.cc
index 5b28c1df..8a94102 100644
--- a/components/tabs/tab_strip_collection.cc
+++ b/components/tabs/tab_strip_collection.cc
@@ -375,19 +375,6 @@
   parent_collection->MaybeRemoveCollection(split).reset();
 }
 
-void TabStripCollection::MoveSplit(split_tabs::SplitTabId split_id,
-                                   bool pinned) {
-  SplitTabCollection* split = GetSplitTabCollection(split_id);
-  std::unique_ptr<TabCollection> removed_collection =
-      split->GetParentCollection()->MaybeRemoveCollection(split);
-  if (pinned) {
-    pinned_collection()->AddCollection(std::move(removed_collection),
-                                       pinned_collection()->ChildCount());
-  } else {
-    unpinned_collection()->AddCollection(std::move(removed_collection), 0);
-  }
-}
-
 void TabStripCollection::InsertSplitTabAt(
     std::unique_ptr<SplitTabCollection> split_collection,
     int index,
diff --git a/components/viz/host/gpu_host_impl.h b/components/viz/host/gpu_host_impl.h
index dec0501..ff6bba16 100644
--- a/components/viz/host/gpu_host_impl.h
+++ b/components/viz/host/gpu_host_impl.h
@@ -7,7 +7,6 @@
 
 #include <map>
 #include <optional>
-#include <queue>
 #include <set>
 #include <string>
 #include <vector>
diff --git a/components/viz/service/display/readback_pixeltest.cc b/components/viz/service/display/readback_pixeltest.cc
index 5a878ab..1e971e68 100644
--- a/components/viz/service/display/readback_pixeltest.cc
+++ b/components/viz/service/display/readback_pixeltest.cc
@@ -1017,7 +1017,9 @@
 
   // Create and wait on shared image interface sync token to wait for shared
   // image creation.
-  ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
+  std::unique_ptr<gpu::RasterScopedAccess> ri_access =
+      shared_image->BeginRasterAccess(ri, shared_image->creation_sync_token(),
+                                      /*readonly=*/false);
 
   SkYUVAInfo info =
       SkYUVAInfo({source_size.width(), source_size.height()},
@@ -1028,9 +1030,9 @@
   ri->WritePixelsYUV(shared_image->mailbox(), yuv_pixmap);
 
   gpu::Mailbox mailbox = shared_image->mailbox();
-  gpu::SyncToken sync_token;
   // Create and wait on raster interface sync token for write pixels YUV.
-  ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+  gpu::SyncToken sync_token =
+      gpu::RasterScopedAccess::EndAccess(std::move(ri_access));
 
   std::unique_ptr<CopyOutputResult> result = IssueCopyOutputRequestAndRender(
       RequestFormat(), RequestDestination(),
diff --git a/components/viz/service/display_embedder/software_output_surface.h b/components/viz/service/display_embedder/software_output_surface.h
index 014a17a..9ff12d2 100644
--- a/components/viz/service/display_embedder/software_output_surface.h
+++ b/components/viz/service/display_embedder/software_output_surface.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SOFTWARE_OUTPUT_SURFACE_H_
 
 #include <memory>
-#include <queue>
 #include <vector>
 
 #include "base/memory/raw_ptr.h"
diff --git a/content/browser/code_cache/generated_code_cache.h b/content/browser/code_cache/generated_code_cache.h
index c5fb054..94602e2 100644
--- a/content/browser/code_cache/generated_code_cache.h
+++ b/content/browser/code_cache/generated_code_cache.h
@@ -6,7 +6,6 @@
 #define CONTENT_BROWSER_CODE_CACHE_GENERATED_CODE_CACHE_H_
 
 #include <map>
-#include <queue>
 
 #include "base/containers/queue.h"
 #include "base/files/file_path.h"
diff --git a/content/browser/code_cache/generated_code_cache_browsertest.cc b/content/browser/code_cache/generated_code_cache_browsertest.cc
index c810c580..f4d0fc89 100644
--- a/content/browser/code_cache/generated_code_cache_browsertest.cc
+++ b/content/browser/code_cache/generated_code_cache_browsertest.cc
@@ -414,6 +414,85 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_P(CodeCacheBrowserTest, CachingFromIFrame) {
+  GURL a_com_parent_page =
+      embedded_test_server()->GetURL("a.com", "/empty.html");
+  const std::string_view kLoadCacheableJSInIframeScript = R"(
+    (async () => {
+      await new Promise(resolve => {
+        const iframe = document.createElement('iframe');
+        document.body.appendChild(iframe);
+        const script = iframe.contentWindow.document.createElement('script');
+        script.addEventListener('load', resolve);
+        script.src = '/cacheable.js';
+        iframe.contentWindow.document.body.appendChild(script);
+      });
+    })();
+  )";
+
+  {
+    // Navigate to the parent page and load an iframe that requests a cacheable
+    // javascript resource (/cacheable.js) in subframe.
+    base::HistogramTester histogram_tester;
+    EXPECT_TRUE(NavigateToURL(shell(), a_com_parent_page));
+
+    EXPECT_TRUE(ExecJs(shell()->web_contents()->GetPrimaryMainFrame(),
+                       kLoadCacheableJSInIframeScript));
+
+    FetchHistogramsFromChildProcesses();
+
+    histogram_tester.ExpectBucketCount(
+        "SiteIsolatedCodeCache.JS.Behaviour",
+        GeneratedCodeCache::CacheEntryStatus::kMiss, 1);
+    histogram_tester.ExpectBucketCount(
+        "SiteIsolatedCodeCache.JS.Behaviour",
+        GeneratedCodeCache::CacheEntryStatus::kCreate, 1);
+    histogram_tester.ExpectBucketCount(
+        "SiteIsolatedCodeCache.JS.Behaviour",
+        GeneratedCodeCache::CacheEntryStatus::kHit, 0);
+
+    PurgeResourceCacheFromTheFirstSubFrame();
+  }
+  {
+    // Navigate to the same test page again, code cache will be produced.
+    base::HistogramTester histogram_tester;
+    EXPECT_TRUE(NavigateToURL(shell(), a_com_parent_page));
+
+    EXPECT_TRUE(ExecJs(shell()->web_contents()->GetPrimaryMainFrame(),
+                       kLoadCacheableJSInIframeScript));
+
+    FetchHistogramsFromChildProcesses();
+
+    histogram_tester.ExpectBucketCount(
+        "SiteIsolatedCodeCache.JS.Behaviour",
+        GeneratedCodeCache::CacheEntryStatus::kMiss, 0);
+    histogram_tester.ExpectBucketCount(
+        "SiteIsolatedCodeCache.JS.Behaviour",
+        GeneratedCodeCache::CacheEntryStatus::kHit, 1);
+
+    PurgeResourceCacheFromTheFirstSubFrame();
+  }
+  {
+    // Navigate to the same test page again, code cache will be consumed.
+    base::HistogramTester histogram_tester;
+    EXPECT_TRUE(NavigateToURL(shell(), a_com_parent_page));
+
+    EXPECT_TRUE(ExecJs(shell()->web_contents()->GetPrimaryMainFrame(),
+                       kLoadCacheableJSInIframeScript));
+
+    FetchHistogramsFromChildProcesses();
+
+    histogram_tester.ExpectBucketCount(
+        "SiteIsolatedCodeCache.JS.Behaviour",
+        GeneratedCodeCache::CacheEntryStatus::kMiss, 0);
+    histogram_tester.ExpectBucketCount(
+        "SiteIsolatedCodeCache.JS.Behaviour",
+        GeneratedCodeCache::CacheEntryStatus::kHit, 1);
+
+    PurgeResourceCacheFromTheFirstSubFrame();
+  }
+}
+
 IN_PROC_BROWSER_TEST_P(CodeCacheBrowserTest,
                        CachingFromThirdPartySharedWorkers) {
   if (!SupportsSharedWorker()) {
diff --git a/content/browser/compute_pressure/pressure_service_base.cc b/content/browser/compute_pressure/pressure_service_base.cc
index 0902b3f2..dc9e979 100644
--- a/content/browser/compute_pressure/pressure_service_base.cc
+++ b/content/browser/compute_pressure/pressure_service_base.cc
@@ -172,6 +172,9 @@
   if (pressure_client.is_client_associated_remote_bound()) {
     manager_receiver_.ReportBadMessage(
         "PressureClientImpl is already connected.");
+    // manager_receiver_.ReportBadMessage() will reset `manager_receiver_` and
+    // so clean up as if the pipe had been disconnected.
+    OnPressureManagerDisconnected();
     return;
   }
 
diff --git a/content/browser/compute_pressure/pressure_service_for_frame_unittest.cc b/content/browser/compute_pressure/pressure_service_for_frame_unittest.cc
index 31ffb9f..ad88b9e9 100644
--- a/content/browser/compute_pressure/pressure_service_for_frame_unittest.cc
+++ b/content/browser/compute_pressure/pressure_service_for_frame_unittest.cc
@@ -257,6 +257,36 @@
   EXPECT_FALSE(pressure_service->IsManagerReceiverBoundForTesting());
 }
 
+TEST_F(PressureServiceForFrameTest, AddClientTwiceFailsAndAddClient) {
+  FakePressureClient client1;
+  ASSERT_EQ(AddPressureClient(&client1, PressureSource::kCpu),
+            device::mojom::PressureManagerAddClientResult::kOk);
+
+  // Simulate the renderer calling AddClient twice for the same PressureSource
+  // and wait for PressureServiceBase to reject the call.
+  FakePressureClient client2;
+  mojo::test::BadMessageObserver bad_message_observer;
+  pressure_manager_->AddClient(
+      PressureSource::kCpu, client2.receiver().BindNewEndpointAndPassRemote(),
+      base::DoNothing());
+
+  EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+            "PressureClientImpl is already connected.");
+
+  auto* pressure_service =
+      PressureServiceForFrame::GetOrCreateForCurrentDocument(
+          contents()->GetPrimaryMainFrame());
+  EXPECT_FALSE(pressure_service->IsManagerReceiverBoundForTesting());
+
+  // Reconnect WebPressureManager.
+  SetPressureServiceForFrame();
+
+  // Add new client to verify that interfaces are functional.
+  FakePressureClient client3;
+  ASSERT_EQ(AddPressureClient(&client3, PressureSource::kCpu),
+            device::mojom::PressureManagerAddClientResult::kOk);
+}
+
 TEST_F(PressureServiceForFrameTest, DisconnectFromBlink) {
   FakePressureClient client;
   ASSERT_EQ(AddPressureClient(&client, PressureSource::kCpu),
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 9155fab..2968dc2 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -326,6 +326,9 @@
 #if BUILDFLAG(WEBNN_USE_TFLITE)
     switches::kWebNNTfliteDumpModel,
 #endif
+#if BUILDFLAG(IS_WIN)
+    switches::kWebNNOrtLoggingLevel,
+#endif
 };
 
 // These values are persisted to logs. Entries should not be renumbered and
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index 331ffdf..2151921 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -1869,12 +1869,14 @@
     GURL render_url;
     std::set<GURL> component_render_urls;
     base::Value::Dict additional_params;
+    std::optional<std::string> seller_tkv_signals;
 
     bool operator<(const ScoringPartitionInfo& other) const {
       return std::tie(partition_id, render_url, component_render_urls,
-                      additional_params) <
+                      additional_params, seller_tkv_signals) <
              std::tie(other.partition_id, other.render_url,
-                      other.component_render_urls, other.additional_params);
+                      other.component_render_urls, other.additional_params,
+                      other.seller_tkv_signals);
     }
   };
 
@@ -2045,7 +2047,10 @@
           partitions.emplace_back(ScoringPartitionInfo{
               partition.partition_id, *partition.render_url,
               *partition.component_render_urls,
-              partition.additional_params->Clone()});
+              partition.additional_params->Clone(),
+              partition.seller_tkv_signals == nullptr
+                  ? std::nullopt
+                  : std::make_optional(*partition.seller_tkv_signals)});
         }
         request_info.compression_groups.emplace(compression_group.first,
                                                 std::move(partitions));
@@ -2337,6 +2342,17 @@
     }
   }
 
+  blink::AuctionConfig::MaybePromiseJson MakeSellerTKVSignals(
+      bool use_promise,
+      const std::optional<std::string>& seller_tkv_signals) {
+    if (use_promise) {
+      return blink::AuctionConfig::MaybePromiseJson::FromPromise();
+    } else {
+      return blink::AuctionConfig::MaybePromiseJson::FromValue(
+          seller_tkv_signals);
+    }
+  }
+
   // Helper to create an auction config with the specified values.
   blink::AuctionConfig CreateAuctionConfig(
       const GURL& seller_decision_logic_url,
@@ -2384,6 +2400,9 @@
     auction_config.non_shared_params.per_buyer_tkv_signals =
         per_buyer_tkv_signals_;
 
+    auction_config.non_shared_params.seller_tkv_signals = MakeSellerTKVSignals(
+        use_promise_for_seller_tkv_signals_, seller_tkv_signals_);
+
     auction_config.non_shared_params.all_buyers_group_limit =
         all_buyers_group_limit_;
     auction_config.non_shared_params.all_buyers_priority_signals =
@@ -3848,6 +3867,7 @@
   bool use_promise_for_buyer_timeouts_ = false;
   bool use_promise_for_buyer_cumulative_timeouts_ = false;
   bool use_promise_for_buyer_currencies_ = false;
+  bool use_promise_for_seller_tkv_signals_ = false;
   bool specify_all_buyer_currency_ = true;
   std::optional<blink::AdCurrency> seller_currency_;
 
@@ -3859,6 +3879,7 @@
   std::map<url::Origin, uint16_t> per_buyer_experiment_group_id_;
   base::flat_map<url::Origin, blink::AuctionConfig::MaybePromiseJson>
       per_buyer_tkv_signals_;
+  std::optional<std::string> seller_tkv_signals_;
   uint16_t all_buyers_group_limit_ = std::numeric_limits<std::uint16_t>::max();
   std::optional<base::flat_map<std::string, double>>
       all_buyers_priority_signals_;
@@ -28284,6 +28305,252 @@
   EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_descriptor->url);
 }
 
+// Test that non promise `seller_tkv_signals` is respected in the case of
+// trusted KVv2 scoring signals. This test completely depends on the checks in
+// MockTrustedSignalsCache that exactly the expected signals requests were made
+// to the cache.
+TEST_P(AuctionRunnerTrustedSignalsTest,
+       TrustedSignalsKVv2NonPromiseSellerTKVSignals) {
+  // Only KVv2 request contains contextual data which is seller_tkv_signals for
+  // scoring signals.
+  if (!UsingKVv2Signals()) {
+    return;
+  }
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      blink::features::kFledgeTrustedSignalsKVv2ContextualData);
+  trusted_scoring_signals_url_ =
+      GURL("https://adstuff.publisher1.com/seller_signals");
+  seller_tkv_signals_ = "signals";
+
+  auction_worklet::AddJavascriptResponse(
+      &url_loader_factory_, kBidder1Url,
+      MakeBidScript(kSeller, "1", "https://ad1.com/", /*num_ad_components=*/0,
+                    kBidder1, kBidder1Name,
+                    /*has_signals=*/true, "k1", "a"));
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         MakeAuctionScript());
+  std::vector<StorageInterestGroup> bidders;
+  bidders.emplace_back(MakeInterestGroup(
+      kBidder1, kBidder1Name, kBidder1Url, kBidder1TrustedSignalsUrl,
+      {"k1", "k2"}, GURL("https://ad1.com"), /*ad_component_urls=*/std::nullopt,
+      coordinator_origin_));
+
+  AddDefaultBidder1SignalsResult();
+
+  auto seller_request_info = IsolatedBidder1SellerRequestInfo();
+  seller_request_info.compression_groups[0][0].component_render_urls = {};
+  seller_request_info.compression_groups[0][0].seller_tkv_signals = "signals";
+  AddScoringSignalsCacheResult(
+      std::move(seller_request_info),
+      MakeCompressionGroupMapForOneGroup(kBidder1ScoringSignalsKVv2Json));
+
+  RunAuctionAndWait(kSellerUrl, std::move(bidders));
+
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_descriptor->url);
+}
+
+// Test that a null non promise `seller_tkv_signals` will not add any contextual
+// data in kvv2 scoring signals request body.
+TEST_P(AuctionRunnerTrustedSignalsTest,
+       TrustedSignalsKVv2NonPromiseNullSellerTKVSignals) {
+  // Only KVv2 request contains contextual data which is seller_tkv_signals for
+  // scoring signals.
+  if (!UsingKVv2Signals()) {
+    return;
+  }
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      blink::features::kFledgeTrustedSignalsKVv2ContextualData);
+  trusted_scoring_signals_url_ =
+      GURL("https://adstuff.publisher1.com/seller_signals");
+  // Exclusively set `seller_tkv_signals_` to nullopt.
+  seller_tkv_signals_ = std::nullopt;
+
+  auction_worklet::AddJavascriptResponse(
+      &url_loader_factory_, kBidder1Url,
+      MakeBidScript(kSeller, "1", "https://ad1.com/", /*num_ad_components=*/0,
+                    kBidder1, kBidder1Name,
+                    /*has_signals=*/true, "k1", "a"));
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         MakeAuctionScript());
+  std::vector<StorageInterestGroup> bidders;
+  bidders.emplace_back(MakeInterestGroup(
+      kBidder1, kBidder1Name, kBidder1Url, kBidder1TrustedSignalsUrl,
+      {"k1", "k2"}, GURL("https://ad1.com"), /*ad_component_urls=*/std::nullopt,
+      coordinator_origin_));
+
+  AddDefaultBidder1SignalsResult();
+
+  auto seller_request_info = IsolatedBidder1SellerRequestInfo();
+  seller_request_info.compression_groups[0][0].component_render_urls = {};
+  AddScoringSignalsCacheResult(
+      std::move(seller_request_info),
+      MakeCompressionGroupMapForOneGroup(kBidder1ScoringSignalsKVv2Json));
+
+  RunAuctionAndWait(kSellerUrl, std::move(bidders));
+
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_descriptor->url);
+}
+
+// Test a `seller_tkv_signals` that is passed via promise during an auction.
+TEST_P(AuctionRunnerTrustedSignalsTest,
+       TrustedSignalsKVv2PromiseSellerTKVSignals) {
+  // Only KVv2 request contains contextual data which is seller_tkv_signals for
+  // scoring signals.
+  if (!UsingKVv2Signals()) {
+    return;
+  }
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      blink::features::kFledgeTrustedSignalsKVv2ContextualData);
+  trusted_scoring_signals_url_ =
+      GURL("https://adstuff.publisher1.com/seller_signals");
+  use_promise_for_seller_tkv_signals_ = true;
+
+  auction_worklet::AddJavascriptResponse(
+      &url_loader_factory_, kBidder1Url,
+      MakeBidScript(kSeller, "1", "https://ad1.com/", /*num_ad_components=*/0,
+                    kBidder1, kBidder1Name,
+                    /*has_signals=*/true, "k1", "a"));
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         MakeAuctionScript());
+  std::vector<StorageInterestGroup> bidders;
+  bidders.emplace_back(MakeInterestGroup(
+      kBidder1, kBidder1Name, kBidder1Url, kBidder1TrustedSignalsUrl,
+      {"k1", "k2"}, GURL("https://ad1.com"), /*ad_component_urls=*/std::nullopt,
+      coordinator_origin_));
+
+  AddDefaultBidder1SignalsResult();
+
+  auto seller_request_info = IsolatedBidder1SellerRequestInfo();
+  seller_request_info.compression_groups[0][0].component_render_urls = {};
+  seller_request_info.compression_groups[0][0].seller_tkv_signals = "signals";
+  AddScoringSignalsCacheResult(
+      std::move(seller_request_info),
+      MakeCompressionGroupMapForOneGroup(kBidder1ScoringSignalsKVv2Json));
+
+  StartAuction(kSellerUrl, std::move(bidders));
+
+  // Can't complete yet.
+  task_environment()->RunUntilIdle();
+  EXPECT_FALSE(auction_run_loop_->AnyQuitCalled());
+
+  // Feed in sellerTKVSignals.
+  abortable_ad_auction_->ResolvedPromiseParam(
+      blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0),
+      blink::mojom::AuctionAdConfigField::kSellerTKVSignals,
+      MakeSellerTKVSignals(/*use_promise=*/false, "signals").value());
+
+  auction_run_loop_->Run();
+
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_descriptor->url);
+}
+
+// Test a null `seller_tkv_signals` that is passed via promise during an
+// auction, and does not add any contextual data in kvv2 scoring signals request
+// body, which can happen when a promise is resolved without passing a value.
+TEST_P(AuctionRunnerTrustedSignalsTest,
+       TrustedSignalsKVv2PromiseNullSellerTKVSignals) {
+  // Only KVv2 request contains contextual data which is seller_tkv_signals for
+  // scoring signals.
+  if (!UsingKVv2Signals()) {
+    return;
+  }
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      blink::features::kFledgeTrustedSignalsKVv2ContextualData);
+  trusted_scoring_signals_url_ =
+      GURL("https://adstuff.publisher1.com/seller_signals");
+  use_promise_for_seller_tkv_signals_ = true;
+
+  auction_worklet::AddJavascriptResponse(
+      &url_loader_factory_, kBidder1Url,
+      MakeBidScript(kSeller, "1", "https://ad1.com/", /*num_ad_components=*/0,
+                    kBidder1, kBidder1Name,
+                    /*has_signals=*/true, "k1", "a"));
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         MakeAuctionScript());
+  std::vector<StorageInterestGroup> bidders;
+  bidders.emplace_back(MakeInterestGroup(
+      kBidder1, kBidder1Name, kBidder1Url, kBidder1TrustedSignalsUrl,
+      {"k1", "k2"}, GURL("https://ad1.com"), /*ad_component_urls=*/std::nullopt,
+      coordinator_origin_));
+
+  AddDefaultBidder1SignalsResult();
+
+  auto seller_request_info = IsolatedBidder1SellerRequestInfo();
+  seller_request_info.compression_groups[0][0].component_render_urls = {};
+  AddScoringSignalsCacheResult(
+      std::move(seller_request_info),
+      MakeCompressionGroupMapForOneGroup(kBidder1ScoringSignalsKVv2Json));
+
+  StartAuction(kSellerUrl, std::move(bidders));
+
+  // Can't complete yet.
+  task_environment()->RunUntilIdle();
+  EXPECT_FALSE(auction_run_loop_->AnyQuitCalled());
+
+  // Feed in sellerTKVSignals.
+  abortable_ad_auction_->ResolvedPromiseParam(
+      blink::mojom::AuctionAdConfigAuctionId::NewMainAuction(0),
+      blink::mojom::AuctionAdConfigField::kSellerTKVSignals,
+      MakeSellerTKVSignals(/*use_promise=*/false, std::nullopt).value());
+
+  auction_run_loop_->Run();
+
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_descriptor->url);
+}
+
+// When the feature is disabled, ensure `seller_tkv_signals` is not included in
+// scoring signals request body.
+TEST_P(AuctionRunnerTrustedSignalsTest,
+       TrustedSignalsKVv2SellerTKVSignalsFeatureDisabled) {
+  // Only KVv2 request contains contextual data which is seller_tkv_signals for
+  // scoring signals.
+  if (!UsingKVv2Signals()) {
+    return;
+  }
+
+  trusted_scoring_signals_url_ =
+      GURL("https://adstuff.publisher1.com/seller_signals");
+  seller_tkv_signals_ = "signals";
+
+  auction_worklet::AddJavascriptResponse(
+      &url_loader_factory_, kBidder1Url,
+      MakeBidScript(kSeller, "1", "https://ad1.com/", /*num_ad_components=*/0,
+                    kBidder1, kBidder1Name,
+                    /*has_signals=*/true, "k1", "a"));
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         MakeAuctionScript());
+  std::vector<StorageInterestGroup> bidders;
+  bidders.emplace_back(MakeInterestGroup(
+      kBidder1, kBidder1Name, kBidder1Url, kBidder1TrustedSignalsUrl,
+      {"k1", "k2"}, GURL("https://ad1.com"), /*ad_component_urls=*/std::nullopt,
+      coordinator_origin_));
+
+  AddDefaultBidder1SignalsResult();
+
+  auto seller_request_info = IsolatedBidder1SellerRequestInfo();
+  seller_request_info.compression_groups[0][0].component_render_urls = {};
+  AddScoringSignalsCacheResult(
+      std::move(seller_request_info),
+      MakeCompressionGroupMapForOneGroup(kBidder1ScoringSignalsKVv2Json));
+
+  RunAuctionAndWait(kSellerUrl, std::move(bidders));
+
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_descriptor->url);
+}
+
 TEST_F(AuctionRunnerTest, TrustedBiddingSignalsJointBatchedRequests) {
   url_loader_factory_.ClearResponses();
   auction_worklet::AddJavascriptResponse(
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc
index 0c71d17..cb3c6c8 100644
--- a/content/browser/interest_group/interest_group_auction.cc
+++ b/content/browser/interest_group/interest_group_auction.cc
@@ -4899,6 +4899,20 @@
   return &it->second;
 }
 
+base::optional_ref<const std::string>
+InterestGroupAuction::GetSellerTKVSignals() const {
+  if (!base::FeatureList::IsEnabled(
+          blink::features::kFledgeTrustedSignalsKVv2ContextualData)) {
+    return std::nullopt;
+  }
+
+  const auto& seller_tkv_signals =
+      config_->non_shared_params.seller_tkv_signals;
+
+  CHECK(!seller_tkv_signals.is_promise());
+  return seller_tkv_signals.value();
+}
+
 std::optional<uint16_t> InterestGroupAuction::GetBuyerExperimentId(
     const blink::AuctionConfig& config,
     const url::Origin& buyer) {
@@ -5793,7 +5807,7 @@
                 bid->interest_group->owner,
                 bid->bid_state->bidder->joining_origin, bid->ad_descriptor.url,
                 bid->GetAdComponentUrls(), std::move(additional_params),
-                /*seller_tkv_signals=*/std::nullopt, partition_id);
+                GetSellerTKVSignals(), partition_id);
     cache_key = auction_worklet::mojom::TrustedSignalsCacheKey::New(
         cache_handle->compression_group_token(), partition_id);
   }
diff --git a/content/browser/interest_group/interest_group_auction.h b/content/browser/interest_group/interest_group_auction.h
index 725f306a..d0620bf7 100644
--- a/content/browser/interest_group/interest_group_auction.h
+++ b/content/browser/interest_group/interest_group_auction.h
@@ -1237,6 +1237,10 @@
   const blink::AuctionConfig::MaybePromiseJson* GetBuyerTKVSignals(
       const url::Origin& buyer) const;
 
+  // Gets the `seller-tkv-signals` in `config` to provide more contextual data
+  // during scoring ads process.
+  base::optional_ref<const std::string> GetSellerTKVSignals() const;
+
   // -----------------------------------
   // Methods not associated with a phase
   // -----------------------------------
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 1a2b84b..6f3e10fc 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -6495,10 +6495,10 @@
   EXPECT_TRUE(console_observer.Wait());
 }
 
-// Exercise rejection path in the renderer for promise-delivered
+// Exercise a rejection path in the renderer for promise-delivered
 // sellerTKVSignals.
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
-                       RunAdAuctionRejectPromiseSellerTKVSignals) {
+                       RunAdAuctionThenRejectPromiseSellerTKVSignals) {
   GURL test_url = embedded_https_test_server().GetURL("a.test", "/echo");
   url::Origin test_origin = url::Origin::Create(test_url);
   ASSERT_TRUE(NavigateToURL(shell(), test_url));
@@ -6520,6 +6520,30 @@
           JsReplace(kAuctionConfigTemplate, test_origin, decision_url)));
 }
 
+// Exercise an initial rejection path in the renderer for promise-delivered
+// sellerTKVSignals.
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
+                       RunAdAuctionAlreadyRejectPromiseSellerTKVSignals) {
+  GURL test_url = embedded_https_test_server().GetURL("a.test", "/echo");
+  url::Origin test_origin = url::Origin::Create(test_url);
+  ASSERT_TRUE(NavigateToURL(shell(), test_url));
+  GURL decision_url = embedded_https_test_server().GetURL(
+      "a.test", "/interest_group/decision_logic.js");
+
+  const char kAuctionConfigTemplate[] = R"({
+      seller: $1,
+      decisionLogicURL: $2,
+      sellerTKVSignals: new Promise((resolve, reject) => { reject('boo'); }),
+      interestGroupBuyers: []
+  })";
+
+  EXPECT_EQ(
+      "TypeError: Failed to execute 'runAdAuction' on 'Navigator': Promise "
+      "argument rejected or resolved to invalid value.",
+      RunAuctionAndWait(
+          JsReplace(kAuctionConfigTemplate, test_origin, decision_url)));
+}
+
 // Exercise error-handling path in the renderer for promise-delivered
 // sellerTKVSignals.
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
@@ -29566,11 +29590,93 @@
     EXPECT_EQ(ad_url, RunAuctionAndWaitForUrl(auction_config));
   }
 
+  void TestSellerTKVSignals(const std::string& expected_render_url_value,
+                            const std::string& seller_tkv_signals) {
+    const char kPublisher[] = "a.test";
+    const char kBidder[] = "b.test";
+    const char kSeller[] = "c.test";
+
+    GURL test_url = embedded_https_test_server().GetURL(
+        kPublisher, "/page_with_iframe.html");
+    GURL ad_url = GURL("https://bar.test/");
+    GURL bidder_url = embedded_https_test_server().GetURL(kBidder, "/echo");
+    GURL bidder_script_url = embedded_https_test_server().GetURL(
+        kBidder, "/interest_group/bidding_logic.js");
+    GURL seller_script_url = embedded_https_test_server().GetURL(
+        kSeller,
+        "/interest_group/decision_logic_trusted_kvv2_scoring_signals.js");
+    GURL seller_signals_url = embedded_https_test_server().GetURL(
+        kSeller, "/trusted_kvv2_scoring_signals");
+
+    ConfigureSignalsServerKeys({url::Origin::Create(seller_signals_url)});
+
+    url::Origin bidder_origin = url::Origin::Create(bidder_script_url);
+    url::Origin seller_origin = url::Origin::Create(seller_script_url);
+
+    // Navigate to bidder site, and add an interest group.
+    ASSERT_TRUE(NavigateToURL(shell(), bidder_url));
+    EXPECT_EQ(kSuccess, JoinInterestGroupAndVerify(
+                            blink::TestInterestGroupBuilder(
+                                /*owner=*/bidder_origin,
+                                /*name=*/"group")
+                                .SetBiddingUrl(bidder_script_url)
+                                .SetAds({{{ad_url, /*metadata=*/std::nullopt}}})
+                                .Build()));
+
+    // Navigate to publisher.
+    ASSERT_TRUE(NavigateToURL(shell(), test_url));
+
+    // Register a seller script that only scores if `trustedScoringSignals` is
+    // successfully fetched.
+    const char kSellerScript[] = R"(
+        function scoreAd(
+            adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals,
+            directFromSellerSignals, crossOriginTrustedScoringSignals) {
+          if ('crossOriginDataVersion' in browserSignals) {
+            throw 'Unexpected crossOriginDataVersion in browserSignals.';
+          }
+
+          if (crossOriginTrustedScoringSignals !== null) {
+            throw 'Unexpected crossOriginTrustedScoringSignals found.';
+          }
+
+          if (trustedScoringSignals.renderURL[browserSignals.renderURL] !== %s) {
+            throw 'Unexpected trustedScoringSignals: ' + JSON.stringify(trustedScoringSignals);
+          }
+
+          return bid;
+        })";
+
+    network_responder_->RegisterNetworkResponse(
+        seller_script_url.path(),
+        base::StringPrintf(kSellerScript, expected_render_url_value.c_str()),
+        "application/javascript");
+
+    const char kAuctionConfig[] =
+        R"({
+            seller: "%s",
+            decisionLogicURL: "%s",
+            trustedScoringSignalsURL: "%s",
+            interestGroupBuyers: ["%s"],
+            trustedScoringSignalsCoordinator: "https://coordinator.test",
+            sellerTKVSignals: %s
+          })";
+
+    std::string auction_config = base::StringPrintf(
+        kAuctionConfig, seller_origin.Serialize().c_str(),
+        seller_script_url.spec().c_str(), seller_signals_url.spec().c_str(),
+        bidder_origin.Serialize().c_str(), seller_tkv_signals.c_str());
+
+    EXPECT_EQ(ad_url, RunAuctionAndWaitForUrl(auction_config));
+  }
+
  protected:
   static std::unique_ptr<net::test_server::HttpResponse>
   HandleTrustedKVv2Signals(const net::test_server::HttpRequest& request) {
     if (!base::StartsWith(request.relative_url,
-                          "/trusted_kvv2_bidding_signals")) {
+                          "/trusted_kvv2_bidding_signals") &&
+        !base::StartsWith(request.relative_url,
+                          "/trusted_kvv2_scoring_signals")) {
       return nullptr;
     }
 
@@ -29596,6 +29702,25 @@
           }
         ])";
 
+    constexpr char kScoringBase[] =
+        R"([
+          {
+            "id": 0,
+            "keyGroupOutputs": [
+              {
+                "tags": [
+                  "renderURLs"
+                ],
+                "keyValues": {
+                  "https://bar.test/": {
+                    "value": "%s"
+                  }
+                }
+              }
+            ]
+          }
+        ])";
+
     // Decrypt the request.
     auto response_key_config = quiche::ObliviousHttpHeaderKeyConfig::Create(
         kTestPrivacySandboxCoordinatorId, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
@@ -29651,8 +29776,16 @@
     CHECK(base::EscapeJSONString(contextual_data,
                                  /*put_in_quotes=*/false,
                                  &escaped_contextual_data));
-    std::string content =
-        base::StringPrintf(kBiddingBase, escaped_contextual_data.c_str());
+    std::string content;
+
+    if (base::StartsWith(request.relative_url,
+                         "/trusted_kvv2_bidding_signals")) {
+      content =
+          base::StringPrintf(kBiddingBase, escaped_contextual_data.c_str());
+    } else {
+      content =
+          base::StringPrintf(kScoringBase, escaped_contextual_data.c_str());
+    }
 
     cbor::Value::MapValue compression_group;
     compression_group.try_emplace(cbor::Value("compressionGroupId"),
@@ -29784,6 +29917,52 @@
                          }))");
 }
 
+IN_PROC_BROWSER_TEST_P(InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+                       SellerTKVSignalsIsInteger) {
+  TestSellerTKVSignals(/*expected_render_url_value=*/"100",
+                       /*seller_tkv_signals=*/"100");
+}
+
+IN_PROC_BROWSER_TEST_P(InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+                       SellerTKVSignalsIsString) {
+  TestSellerTKVSignals(/*expected_render_url_value=*/"\"contextual data\"",
+                       /*seller_tkv_signals=*/"\"contextual data\"");
+}
+
+IN_PROC_BROWSER_TEST_P(InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+                       SellerTKVSignalsIsArray) {
+  TestSellerTKVSignals(/*expected_render_url_value=*/"\"[1, 2, 3]\"",
+                       /*seller_tkv_signals=*/"\"[1, 2, 3]\"");
+}
+
+// For "null" test case, `sellerTKVSignals` behaves differently than
+// `perBuyerTKVSignals` in that it uses IDL to convert from renderer side config
+// to mojom promise value. It seems there is a special handling for "null" in
+// the IDL code, which will result an empty promise value in the end.
+IN_PROC_BROWSER_TEST_P(InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+                       SellerTKVSignalsIsNull) {
+  TestSellerTKVSignals(/*expected_render_url_value=*/kNoContextualDataValue,
+                       /*seller_tkv_signals=*/"null");
+}
+
+// Same as "null" test case, "undefined" also results an empty promise value.
+IN_PROC_BROWSER_TEST_P(InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+                       SellerTKVSignalsIsUndefined) {
+  TestSellerTKVSignals(/*expected_render_url_value=*/kNoContextualDataValue,
+                       /*seller_tkv_signals=*/"undefined");
+}
+
+IN_PROC_BROWSER_TEST_P(InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+                       SellerTKVSignalsRunAdAuctionWithPromise) {
+  TestSellerTKVSignals(/*expected_render_url_value=*/"\"contextual data\"",
+                       /*seller_tkv_signals=*/
+                       R"(new Promise((resolve, reject) => {
+                            setTimeout(
+                                () => { resolve("contextual data"); },
+                                100);
+                       }))");
+}
+
 class DisableKVv2ContextualDataBrowserTest
     : public InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest {
  public:
@@ -29806,6 +29985,12 @@
                          /*buyer_tkv_signals=*/"\"contextual data\"");
 }
 
+IN_PROC_BROWSER_TEST_P(DisableKVv2ContextualDataBrowserTest,
+                       SellerTKVSignalsWithDisabledFeature) {
+  TestSellerTKVSignals(/*expected_render_url_value=*/kNoContextualDataValue,
+                       /*seller_tkv_signals=*/"\"contextual data\"");
+}
+
 class DisableLocalAuctionInterestGroupBrowserTest
     : public InterestGroupBrowserTest {
  public:
diff --git a/content/browser/loader/keep_alive_attribution_request_helper_unittest.cc b/content/browser/loader/keep_alive_attribution_request_helper_unittest.cc
index b3aa9700..bd22661 100644
--- a/content/browser/loader/keep_alive_attribution_request_helper_unittest.cc
+++ b/content/browser/loader/keep_alive_attribution_request_helper_unittest.cc
@@ -421,8 +421,8 @@
 
   {  // kAttributionReportingInBrowserMigration disabled
     scoped_feature_list().Reset();
-    scoped_feature_list().InitAndEnableFeature(
-        blink::features::kKeepAliveInBrowserMigration);
+    scoped_feature_list().InitAndDisableFeature(
+        blink::features::kAttributionReportingInBrowserMigration);
     const GURL source_url("https://secure.test");
     test_web_contents()->NavigateAndCommit(source_url);
 
diff --git a/content/browser/loader/keep_alive_url_loader.h b/content/browser/loader/keep_alive_url_loader.h
index 0ee60cdf..394344b9 100644
--- a/content/browser/loader/keep_alive_url_loader.h
+++ b/content/browser/loader/keep_alive_url_loader.h
@@ -9,7 +9,6 @@
 
 #include <memory>
 #include <optional>
-#include <queue>
 #include <string>
 #include <string_view>
 #include <vector>
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index a5caadf..21911ad 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -1037,7 +1037,10 @@
       std::move(factory), std::move(throttles), global_request_id_.request_id,
       options, resource_request_.get(), /*client=*/this,
       kNavigationUrlLoaderTrafficAnnotation,
-      GetUIThreadTaskRunner({BrowserTaskType::kNavigationNetworkResponse}));
+      GetUIThreadTaskRunner({BrowserTaskType::kNavigationNetworkResponse}),
+      /*cors_exempt_header_list=*/std::nullopt,
+      /*client_receiver_delegate=*/nullptr,
+      &request_info_->common_params->initiator_origin_trial_features);
 }
 
 void NavigationURLLoaderImpl::OnReceiveEarlyHints(
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc
index 10e9b3c..01349d33 100644
--- a/content/browser/preloading/prerender/prerender_browsertest.cc
+++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -5555,8 +5555,8 @@
   ShellContentBrowserClient::Get()
       ->set_create_throttles_for_navigation_callback(base::BindLambdaForTesting(
           [&throttle](NavigationThrottleRegistry& registry) -> void {
-            auto throttle_ptr = std::make_unique<TestNavigationThrottle>(
-                &registry.GetNavigationHandle());
+            auto throttle_ptr =
+                std::make_unique<TestNavigationThrottle>(registry);
             CHECK(!throttle);
             throttle = throttle_ptr.get();
             throttle_ptr->SetResponse(
diff --git a/content/browser/renderer_host/back_forward_cache_subframe_navigation_throttle_unittest.cc b/content/browser/renderer_host/back_forward_cache_subframe_navigation_throttle_unittest.cc
index 117b2a1..0e3c54a2 100644
--- a/content/browser/renderer_host/back_forward_cache_subframe_navigation_throttle_unittest.cc
+++ b/content/browser/renderer_host/back_forward_cache_subframe_navigation_throttle_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/bind.h"
 #include "content/public/test/test_navigation_throttle.h"
+#include "content/public/test/test_navigation_throttle_inserter.h"
 #include "content/test/navigation_simulator_impl.h"
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_web_contents.h"
@@ -46,7 +47,7 @@
   // Subclass should override this method to achieve desired behavior. See
   // `CreatePausedSubframeNavigationRequest`.
   void DidStartNavigation(NavigationHandle* handle) override {
-    NOTIMPLEMENTED();
+    ASSERT_TRUE(false) << "DidStartNavigation should not be called";
   }
 
   // Returns `BackForwardCacheSubframeNavigationThrottle` for a paused subframe
@@ -107,19 +108,29 @@
   void SetUp() override {
     BackForwardCacheSubframeNavigationThrottleTestBase::SetUp();
     Observe(contents());
+
+    throttle_inserter_ = std::make_unique<TestNavigationThrottleInserter>(
+        contents(),
+        base::BindLambdaForTesting(
+            [&](NavigationThrottleRegistry& registry) -> void {
+              // Defers subframe navigations on `WillCommitWithoutUrlLoader` by
+              // registering a `TestNavigationThrottle`, so that
+              // NavigationRequest created in each test from
+              // `CreatePausedSubframeNavigationRequest` wouldn't commit
+              // immediately.
+              auto throttle =
+                  std::make_unique<TestNavigationThrottle>(registry);
+              throttle->SetResponse(
+                  TestNavigationThrottle::WILL_COMMIT_WITHOUT_URL_LOADER,
+                  TestNavigationThrottle::SYNCHRONOUS,
+                  NavigationThrottle::DEFER);
+              registry.AddThrottle(std::move(throttle));
+            }));
   }
 
-  // Defers subframe navigations on `WillCommitWithoutUrlLoader` by
-  // registering a `TestNavigationThrottle`, so that NavigationRequest created
-  // in each test from `CreatePausedSubframeNavigationRequest` wouldn't commit
-  // immediately.
   void DidStartNavigation(NavigationHandle* handle) override {
-    auto throttle = std::make_unique<TestNavigationThrottle>(handle);
-    throttle->SetResponse(
-        TestNavigationThrottle::WILL_COMMIT_WITHOUT_URL_LOADER,
-        TestNavigationThrottle::SYNCHRONOUS, NavigationThrottle::DEFER);
-    handle->RegisterThrottleForTesting(
-        std::unique_ptr<TestNavigationThrottle>(std::move(throttle)));
+    // Override to hide the parent class's ASSERT_TRUE(false) to permit the
+    // callback.
   }
 
   GURL GetNavigationTargetURL() override {
@@ -128,6 +139,7 @@
 
  private:
   static constexpr char kSubframeNavigateWithoutURLLoaderURL[] = "about:blank";
+  std::unique_ptr<TestNavigationThrottleInserter> throttle_inserter_;
 };
 
 TEST_F(BackForwardCacheSubframeNavigationThrottleWithoutUrlLoaderTest,
@@ -227,7 +239,10 @@
 class BackForwardCacheSubframeNavigationThrottleTest
     : public BackForwardCacheSubframeNavigationThrottleTestBase {
  protected:
-  void DidStartNavigation(NavigationHandle* handle) override {}
+  void DidStartNavigation(NavigationHandle* handle) override {
+    // Override to hide the parent class's ASSERT_TRUE(false) to permit the
+    // callback.
+  }
 
   GURL GetNavigationTargetURL() override {
     return GURL(kSubframeNavigateWithURLLoaderURL);
diff --git a/content/browser/renderer_host/cookie_browsertest.cc b/content/browser/renderer_host/cookie_browsertest.cc
index 8d1bab0..b8f8a7b2 100644
--- a/content/browser/renderer_host/cookie_browsertest.cc
+++ b/content/browser/renderer_host/cookie_browsertest.cc
@@ -425,11 +425,6 @@
 // embedded_test_server() uses http, which is insecure, but localhost is
 // allowed to set prefixed cookies anyway.
 IN_PROC_BROWSER_TEST_P(CookieBrowserTest, PrefixedCookies_Write_Localhost) {
-  if (GetCookiesOnSetEnabled() && AsyncSetCookieEnabled()) {
-    GTEST_SKIP()
-        << "Skipping known-flaky configuration, see crbug.com/417674996";
-  }
-
   ASSERT_TRUE(embedded_test_server()->Start());
   EXPECT_TRUE(NavigateToURL(
       shell(), embedded_test_server()->GetURL("localhost", "/empty.html")));
diff --git a/content/browser/renderer_host/navigation_request_unittest.cc b/content/browser/renderer_host/navigation_request_unittest.cc
index ec92738..3ee00512 100644
--- a/content/browser/renderer_host/navigation_request_unittest.cc
+++ b/content/browser/renderer_host/navigation_request_unittest.cc
@@ -162,8 +162,8 @@
   // synchronously return |result| on checks by default.
   TestNavigationThrottle* CreateTestNavigationThrottle(
       NavigationThrottle::ThrottleCheckResult result) {
-    TestNavigationThrottle* test_throttle =
-        new TestNavigationThrottle(GetNavigationRequest());
+    TestNavigationThrottle* test_throttle = new TestNavigationThrottle(
+        *GetNavigationRequest()->GetNavigationThrottleRunnerForTesting());
     test_throttle->SetResponseForAllMethods(TestNavigationThrottle::SYNCHRONOUS,
                                             result);
     GetNavigationRequest()->RegisterThrottleForTesting(
diff --git a/content/browser/renderer_host/navigation_throttle_runner_unittest.cc b/content/browser/renderer_host/navigation_throttle_runner_unittest.cc
index 2a659774..78c742a 100644
--- a/content/browser/renderer_host/navigation_throttle_runner_unittest.cc
+++ b/content/browser/renderer_host/navigation_throttle_runner_unittest.cc
@@ -12,6 +12,7 @@
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/mock_navigation_handle.h"
+#include "content/public/test/mock_navigation_throttle_registry.h"
 #include "content/public/test/test_navigation_throttle.h"
 #include "content/public/test/test_renderer_host.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
@@ -22,9 +23,9 @@
 // called.
 class DeletingNavigationThrottle : public NavigationThrottle {
  public:
-  DeletingNavigationThrottle(NavigationHandle* handle,
+  DeletingNavigationThrottle(NavigationThrottleRegistry& registry,
                              const base::RepeatingClosure& deletion_callback)
-      : NavigationThrottle(handle), deletion_callback_(deletion_callback) {}
+      : NavigationThrottle(registry), deletion_callback_(deletion_callback) {}
   ~DeletingNavigationThrottle() override {}
 
   NavigationThrottle::ThrottleCheckResult WillStartRequest() override {
@@ -69,8 +70,7 @@
 
   void SetUp() override {
     RenderViewHostTestHarness::SetUp();
-    runner_ =
-        std::make_unique<NavigationThrottleRunner>(this, 1, true);
+    runner_ = std::make_unique<NavigationThrottleRunner>(this, 1, true);
   }
 
   void Resume() { runner_->CallResumeForTesting(); }
@@ -153,7 +153,7 @@
   TestNavigationThrottle* CreateTestNavigationThrottle(
       NavigationThrottle::ThrottleCheckResult result) {
     TestNavigationThrottle* test_throttle =
-        new TestNavigationThrottle(&handle_);
+        new TestNavigationThrottle(registry_);
     test_throttle->SetResponseForAllMethods(TestNavigationThrottle::SYNCHRONOUS,
                                             result);
     runner_->AddThrottle(
@@ -178,7 +178,7 @@
   // NavigationHandle in checks.
   void AddDeletingNavigationThrottle() {
     runner_->AddThrottle(std::make_unique<DeletingNavigationThrottle>(
-        &handle_,
+        registry_,
         base::BindRepeating(
             &NavigationThrottleRunnerTest::ResetNavigationThrottleRunner,
             base::Unretained(this))));
@@ -199,8 +199,15 @@
 
   void ResetNavigationThrottleRunner() { runner_.reset(); }
 
-  std::unique_ptr<NavigationThrottleRunner> runner_;
   MockNavigationHandle handle_;
+  // Used to construct NavigationThrottle inheritances as the `runner` instance
+  // created in this test doesn't handle GetNavigationHandle() correctly for an
+  // unexpected downcast.
+  // TODO(https://crbug.com/412524375) Should be fixed together with the
+  // `Delegate` issue, tried at https://crrev.com/c/6508568.
+  MockNavigationThrottleRegistry registry_ =
+      MockNavigationThrottleRegistry(&handle_);
+  std::unique_ptr<NavigationThrottleRunner> runner_;
   NavigationThrottleRunner::Event observer_last_event_ =
       NavigationThrottleRunner::Event::kNoEvent;
   bool was_delegate_notified_ = false;
diff --git a/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h b/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
index a022194..ae32f6ca 100644
--- a/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
+++ b/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
@@ -7,7 +7,6 @@
 
 #include <stdint.h>
 
-#include <queue>
 #include <string>
 #include <vector>
 
diff --git a/content/browser/renderer_host/pepper/pepper_network_proxy_host.h b/content/browser/renderer_host/pepper/pepper_network_proxy_host.h
index eacf6a808..ccf92712c 100644
--- a/content/browser/renderer_host/pepper/pepper_network_proxy_host.h
+++ b/content/browser/renderer_host/pepper/pepper_network_proxy_host.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include <optional>
-#include <queue>
 #include <set>
 #include <string>
 
diff --git a/content/browser/webid/fedcm_accounts_fetcher.cc b/content/browser/webid/fedcm_accounts_fetcher.cc
index 2215260e..0ba3094 100644
--- a/content/browser/webid/fedcm_accounts_fetcher.cc
+++ b/content/browser/webid/fedcm_accounts_fetcher.cc
@@ -345,7 +345,7 @@
         accounts.begin(), accounts.end(),
         [&](const IdentityRequestAccountPtr& account) {
           return !account->is_filtered_out &&
-                 !federated_auth_request_impl_->HadAccoundIdBeforeLogin(
+                 !federated_auth_request_impl_->HadAccountIdBeforeLogin(
                      account->id);
         });
     if (new_unfiltered > 0u) {
diff --git a/content/browser/webid/fedcm_url_computations.cc b/content/browser/webid/fedcm_url_computations.cc
index db9e078..77cc263d 100644
--- a/content/browser/webid/fedcm_url_computations.cc
+++ b/content/browser/webid/fedcm_url_computations.cc
@@ -8,7 +8,6 @@
 
 #include "base/containers/contains.h"
 #include "base/strings/escape.h"
-#include "base/strings/strcat.h"
 #include "content/browser/webid/fedcm_mappers.h"
 #include "content/browser/webid/flags.h"
 #include "content/browser/webid/sd_jwt.h"
@@ -29,18 +28,6 @@
 
 }  // namespace
 
-std::string ComputeUrlEncodedTokenPostDataForIssuers(
-    const std::string& account_id,
-    const sdjwt::Jwk& holder_key,
-    const std::string& format) {
-  return base::StrCat(
-      {"account_id=", base::EscapeUrlEncodedData(account_id, /*use_plus=*/true),
-       "&holder_key=",
-       base::EscapeUrlEncodedData(*holder_key.Serialize(),
-                                  /*use_plus=*/true),
-       "&format=", base::EscapeUrlEncodedData(format, /*use_plus=*/true)});
-}
-
 std::string ComputeUrlEncodedTokenPostData(
     RenderFrameHost& render_frame_host,
     const std::string& client_id,
diff --git a/content/browser/webid/fedcm_url_computations.h b/content/browser/webid/fedcm_url_computations.h
index 91f3c541..ec7102c 100644
--- a/content/browser/webid/fedcm_url_computations.h
+++ b/content/browser/webid/fedcm_url_computations.h
@@ -17,12 +17,6 @@
 
 // This file contains functions that compute URLs that are used in FedCM.
 
-// Computes the URL-encoded POST data for the token endpoint for issuers.
-std::string ComputeUrlEncodedTokenPostDataForIssuers(
-    const std::string& account_id,
-    const sdjwt::Jwk& holder_key,
-    const std::string& format);
-
 // Computes the URL-encoded POST data for the token endpoint.
 std::string ComputeUrlEncodedTokenPostData(
     RenderFrameHost& render_frame_host,
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 01bc163..6f6aa6df 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -1407,8 +1407,8 @@
     endpoint = idp_info.endpoints.issuance;
     federated_sdjwt_handler_ = std::make_unique<FederatedSdJwtHandler>(
         idp_info.provider, render_frame_host(), this);
-    query = ComputeUrlEncodedTokenPostDataForIssuers(
-        account_id, federated_sdjwt_handler_->GetPublicKey(), "vc+sd-jwt");
+    query = federated_sdjwt_handler_->ComputeUrlEncodedTokenPostDataForIssuers(
+        account_id);
   } else {
     endpoint = idp_info.endpoints.token;
     query = ComputeUrlEncodedTokenPostData(
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index 5e78994..709df42 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -252,7 +252,7 @@
     return token_request_get_infos_;
   }
   GURL login_url() { return login_url_; }
-  bool HadAccoundIdBeforeLogin(const std::string& account_id) {
+  bool HadAccountIdBeforeLogin(const std::string& account_id) {
     return account_ids_before_login_.contains(account_id);
   }
   // Return the FedCmMetrics for use by FedCmAccountsFetcher.
diff --git a/content/browser/webid/federated_sd_jwt_handler.cc b/content/browser/webid/federated_sd_jwt_handler.cc
index 31a5091..c19539bc 100644
--- a/content/browser/webid/federated_sd_jwt_handler.cc
+++ b/content/browser/webid/federated_sd_jwt_handler.cc
@@ -11,6 +11,8 @@
 #include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
+#include "base/strings/escape.h"
+#include "base/strings/strcat.h"
 #include "base/time/time.h"
 #include "content/browser/webid/fedcm_mappers.h"
 #include "content/browser/webid/federated_auth_request_impl.h"
@@ -55,6 +57,16 @@
 
 FederatedSdJwtHandler::~FederatedSdJwtHandler() {}
 
+std::string FederatedSdJwtHandler::ComputeUrlEncodedTokenPostDataForIssuers(
+    const std::string& account_id) {
+  return base::StrCat(
+      {"account_id=", base::EscapeUrlEncodedData(account_id, /*use_plus=*/true),
+       "&holder_key=",
+       base::EscapeUrlEncodedData(*GetPublicKey().Serialize(),
+                                  /*use_plus=*/true),
+       "&format=", base::EscapeUrlEncodedData("vc+sd-jwt", /*use_plus=*/true)});
+}
+
 void FederatedSdJwtHandler::ProcessSdJwt(const std::string& token) {
   // Checked previously.
   DCHECK(IsFedCmDelegationEnabled());
diff --git a/content/browser/webid/federated_sd_jwt_handler.h b/content/browser/webid/federated_sd_jwt_handler.h
index f95565a0..b9d718a 100644
--- a/content/browser/webid/federated_sd_jwt_handler.h
+++ b/content/browser/webid/federated_sd_jwt_handler.h
@@ -27,11 +27,13 @@
       FederatedAuthRequestImpl* federated_auth_request_impl);
   ~FederatedSdJwtHandler();
 
+  std::string ComputeUrlEncodedTokenPostDataForIssuers(
+      const std::string& account_id);
+
   void ProcessSdJwt(const std::string& token);
 
-  sdjwt::Jwk GetPublicKey() const;
-
  private:
+  sdjwt::Jwk GetPublicKey() const;
   void OnDisclosureParsed(base::RepeatingClosure cb,
                           const std::string& json,
                           data_decoder::DataDecoder::ValueOrError result);
diff --git a/content/common/features.cc b/content/common/features.cc
index 8c02eee..f51f7af 100644
--- a/content/common/features.cc
+++ b/content/common/features.cc
@@ -190,13 +190,6 @@
              "ExperimentalContentSecurityPolicyFeatures",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// For cross-site iframes, sends the top-level origin to the IDP and parses
-// an optional returned boolean indicating whether it is part of the same
-// client to allow for UI decisions based on the boolean.
-BASE_FEATURE(kFedCmIframeOrigin,
-             "FedCmIframeOrigin",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // Whether to support the newer syntax for the "Use Other Account"
 // and account labels features.
 BASE_FEATURE(kFedCmUseOtherAccountAndLabelsNewSyntax,
diff --git a/content/common/features.h b/content/common/features.h
index 032df239..da9eacc 100644
--- a/content/common/features.h
+++ b/content/common/features.h
@@ -66,7 +66,6 @@
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kEmbeddingRequiresOptIn);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kExperimentalContentSecurityPolicyFeatures);
-CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmIframeOrigin);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmUseOtherAccountAndLabelsNewSyntax);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmSameSiteLax);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFilterInstalledAppsWebAppMatching);
diff --git a/content/gpu/gpu_child_thread.h b/content/gpu/gpu_child_thread.h
index be7772a..1dba2ee 100644
--- a/content/gpu/gpu_child_thread.h
+++ b/content/gpu/gpu_child_thread.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include <memory>
-#include <queue>
 #include <vector>
 
 #include "base/command_line.h"
diff --git a/content/public/browser/tts_controller.h b/content/public/browser/tts_controller.h
index 83ffd91..23f3d8c 100644
--- a/content/public/browser/tts_controller.h
+++ b/content/public/browser/tts_controller.h
@@ -6,7 +6,6 @@
 #define CONTENT_PUBLIC_BROWSER_TTS_CONTROLLER_H_
 
 #include <memory>
-#include <queue>
 #include <set>
 #include <string>
 #include <vector>
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 581513f..d0f189d63 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -422,6 +422,13 @@
              "FedCmIdPregistration",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// For cross-site iframes, sends the top-level origin to the IDP and parses
+// an optional returned boolean indicating whether it is part of the same
+// client to allow for UI decisions based on the boolean.
+BASE_FEATURE(kFedCmIframeOrigin,
+             "FedCmIframeOrigin",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enables usage of the FedCM API with metrics endpoint at the same time.
 BASE_FEATURE(kFedCmMetricsEndpoint,
              "FedCmMetricsEndpoint",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 334bba27..5cf8fd3 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -109,6 +109,7 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmCooldownOnIgnore);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmDelegation);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmIdPRegistration);
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmIframeOrigin);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmMetricsEndpoint);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmMultipleIdentityProviders);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmShowFilteredAccounts);
diff --git a/content/public/test/test_navigation_throttle.cc b/content/public/test/test_navigation_throttle.cc
index 21175c9..32340c5 100644
--- a/content/public/test/test_navigation_throttle.cc
+++ b/content/public/test/test_navigation_throttle.cc
@@ -12,9 +12,6 @@
 
 namespace content {
 
-TestNavigationThrottle::TestNavigationThrottle(NavigationHandle* handle)
-    : NavigationThrottle(handle) {}
-
 TestNavigationThrottle::TestNavigationThrottle(
     NavigationThrottleRegistry& registry)
     : NavigationThrottle(registry) {}
diff --git a/content/public/test/test_navigation_throttle.h b/content/public/test/test_navigation_throttle.h
index b6617b65..520858d1 100644
--- a/content/public/test/test_navigation_throttle.h
+++ b/content/public/test/test_navigation_throttle.h
@@ -13,8 +13,6 @@
 
 namespace content {
 
-class NavigationHandle;
-
 // This class can be used to cancel navigations synchronously or asynchronously
 // at specific times in the NavigationThrottle lifecycle.
 //
@@ -36,11 +34,6 @@
     ASYNCHRONOUS,
   };
 
-  // Note: This legacy constructor will be removed soon. New code should use the
-  // other constructor that takes a NavigationThrottleRegistry&.
-  // TODO(https://crbug.com/412524375): Remove this constructor.
-  explicit TestNavigationThrottle(NavigationHandle* handle);
-
   explicit TestNavigationThrottle(NavigationThrottleRegistry& registry);
 
   TestNavigationThrottle(const TestNavigationThrottle&) = delete;
diff --git a/content/renderer/pepper/pepper_websocket_host.h b/content/renderer/pepper/pepper_websocket_host.h
index 613a911c..a3d0a8c 100644
--- a/content/renderer/pepper/pepper_websocket_host.h
+++ b/content/renderer/pepper/pepper_websocket_host.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include <memory>
-#include <queue>
 
 #include "base/memory/raw_ptr.h"
 #include "ppapi/host/host_message_context.h"
diff --git a/content/shell/browser/shell_platform_delegate_ios.mm b/content/shell/browser/shell_platform_delegate_ios.mm
index e616b01..fd7c193 100644
--- a/content/shell/browser/shell_platform_delegate_ios.mm
+++ b/content/shell/browser/shell_platform_delegate_ios.mm
@@ -33,7 +33,8 @@
 static const char kDetailedGraphicsTracingCategories[] =
     "-*,blink,cc,gpu,renderer.scheduler,sequence_manager,v8,toplevel,viz,evdev,"
     "input,benchmark,disabled-by-default-skia,disabled-by-default-skia.gpu,"
-    "disabled-by-default-skia.gpu.cache,disabled-by-default-skia.shaders";
+    "disabled-by-default-skia.gpu.cache,disabled-by-default-skia.shaders,"
+    "disabled-by-default-gpu.dawn,disabled-by-default-gpu.graphite.dawn";
 
 static const char kNavigationTracingCategories[] =
     "-*,benchmark,toplevel,ipc,base,browser,navigation,omnibox,ui,shutdown,"
diff --git a/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
index 864ce2d..811980b 100644
--- a/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index f9c8c5a..36d06a3 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
index e363ec3..726fa05 100644
--- a/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
index e246935..b091cdb6 100644
--- a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
index 5194753..0524ec71 100644
--- a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
index 3985a919..9b441a2 100644
--- a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 834d61aa..f2511cb 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
index 5194753..0524ec71 100644
--- a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
index 123ed23..492d42c 100644
--- a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index 58b380ee..6437820 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index 52a458c8..3efb6cf 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 600c053..794599f7 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index dc6902d..b1050e3 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/gpu_tests/test_expectations/webrtc_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webrtc_expectations.txt
index 1247f9ad..30f31ad 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webrtc_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webrtc_expectations.txt
@@ -11,7 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/gpu/validate_tag_consistency.py b/content/test/gpu/validate_tag_consistency.py
index d67b96b..0f80064 100755
--- a/content/test/gpu/validate_tag_consistency.py
+++ b/content/test/gpu/validate_tag_consistency.py
@@ -195,7 +195,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
-#         android-brya
+#         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/test/navigation_simulator_unittest.cc b/content/test/navigation_simulator_unittest.cc
index b51096d..cee844b 100644
--- a/content/test/navigation_simulator_unittest.cc
+++ b/content/test/navigation_simulator_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "base/test/test_simple_task_runner.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
@@ -55,6 +56,7 @@
     std::tie(cancel_time_, sync_) = GetParam();
     simulator_ = NavigationSimulator::CreateRendererInitiated(
         GURL("https://example.test"), main_rfh());
+    SetUpThrottleInserter();
   }
 
   void TearDown() override {
@@ -62,22 +64,28 @@
     RenderViewHostImplTestHarness::TearDown();
   }
 
-  void DidStartNavigation(content::NavigationHandle* handle) override {
-    auto throttle = std::make_unique<TestNavigationThrottle>(handle);
-    throttle->SetCallback(
-        TestNavigationThrottle::WILL_FAIL_REQUEST,
-        base::BindRepeating(
-            &CancellingNavigationSimulatorTest::OnWillFailRequestCalled,
-            base::Unretained(this)));
-    if (cancel_time_.has_value()) {
-      throttle->SetResponse(cancel_time_.value(), sync_,
-                            NavigationThrottle::CANCEL);
-    }
-    handle->RegisterThrottleForTesting(
-        std::unique_ptr<TestNavigationThrottle>(std::move(throttle)));
+  void DidStartNavigation(NavigationHandle* handle) override {}
+
+  void SetUpThrottleInserter() {
+    throttle_inserter_ = std::make_unique<TestNavigationThrottleInserter>(
+        RenderViewHostTestHarness::web_contents(),
+        base::BindLambdaForTesting([&](NavigationThrottleRegistry& registry)
+                                       -> void {
+          auto throttle = std::make_unique<TestNavigationThrottle>(registry);
+          throttle->SetCallback(
+              TestNavigationThrottle::WILL_FAIL_REQUEST,
+              base::BindRepeating(
+                  &CancellingNavigationSimulatorTest::OnWillFailRequestCalled,
+                  base::Unretained(this)));
+          if (cancel_time_.has_value()) {
+            throttle->SetResponse(cancel_time_.value(), sync_,
+                                  NavigationThrottle::CANCEL);
+          }
+          registry.AddThrottle(std::move(throttle));
+        }));
   }
 
-  void DidFinishNavigation(content::NavigationHandle* handle) override {
+  void DidFinishNavigation(NavigationHandle* handle) override {
     did_finish_navigation_ = true;
   }
 
@@ -88,6 +96,7 @@
   std::unique_ptr<NavigationSimulator> simulator_;
   bool did_finish_navigation_ = false;
   bool will_fail_request_called_ = false;
+  std::unique_ptr<TestNavigationThrottleInserter> throttle_inserter_;
   base::WeakPtrFactory<CancellingNavigationSimulatorTest> weak_ptr_factory_{
       this};
 };
@@ -110,7 +119,7 @@
     Observe(RenderViewHostImplTestHarness::web_contents());
   }
 
-  void DidFinishNavigation(content::NavigationHandle* handle) override {
+  void DidFinishNavigation(NavigationHandle* handle) override {
     did_finish_navigation_ = true;
     is_post_ = handle->IsPost();
   }
@@ -139,7 +148,7 @@
     Observe(RenderViewHostImplTestHarness::web_contents());
   }
 
-  void DidFinishNavigation(content::NavigationHandle* handle) override {
+  void DidFinishNavigation(NavigationHandle* handle) override {
     EXPECT_TRUE(handle->GetResponseHeaders()->HasHeaderValue("My-Test-Header",
                                                              "my-test-value"));
   }
diff --git a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
index e6ab0c5..c1c6a30 100644
--- a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
@@ -7,9 +7,9 @@
 
 #include <stddef.h>
 #include <stdint.h>
+
 #include <map>
 #include <memory>
-#include <queue>
 #include <string>
 #include <utility>
 #include <vector>
diff --git a/docs/ui/display/multiscreen_testing.md b/docs/ui/display/multiscreen_testing.md
index c4bdbe0..04bd879e 100644
--- a/docs/ui/display/multiscreen_testing.md
+++ b/docs/ui/display/multiscreen_testing.md
@@ -64,3 +64,14 @@
 not explicitly documented here.
 The host must have `xserver-xorg-core`, `xserver-xorg-video-dummy` and `x11-xserver-utils` packages installed. See [`//testing/xvfb.py`](/testing/xvfb.py) for more details on the
 required Xorg/XRandR setup.
+
+#### Headless Chrome
+
+Chrome Headless Mode supports headless screen configuration using `--screen-info` command line switch on Linux, Windows and Mac since m138. The following example starts Chrome in headless mode with a virtual screen that has two displays: a primary one with resolution 1600x1200 pixels, and a secondary one with the same resolution but with portrait orientation and located to the right of the primary display.
+
+```bash
+./chrome --headless --screen-info={1600x1200}{1200x1600}
+```
+
+Please refer to [components/headless/screen_info/README.md](components/headless/screen_info/README.md) document for more information about `--screen-info` command line switch.
+
diff --git a/extensions/browser/service_worker/OWNERS b/extensions/browser/service_worker/OWNERS
index 600166e4..ccdd725 100644
--- a/extensions/browser/service_worker/OWNERS
+++ b/extensions/browser/service_worker/OWNERS
@@ -1,3 +1,4 @@
+andreaorru@chromium.org
 jlulejian@chromium.org
 dbertoni@chromium.org
 rdevlin.cronin@chromium.org
diff --git a/extensions/browser/service_worker/service_worker_task_queue.h b/extensions/browser/service_worker/service_worker_task_queue.h
index 9e9df774..9caf52a 100644
--- a/extensions/browser/service_worker/service_worker_task_queue.h
+++ b/extensions/browser/service_worker/service_worker_task_queue.h
@@ -152,27 +152,6 @@
     auto operator<=>(const SequencedContextId& rhs) const = default;
   };
 
-  // Browser process worker state of an activated extension.
-  enum class BrowserState {
-    // Initial state, not started.
-    kNotStarted,
-    // Worker has completed starting at least once (i.e. has seen
-    // DidStartWorkerForScope).
-    kStarted,
-    // Worker has completed starting at least once and has run all pending
-    // tasks (i.e. has seen DidStartWorkerForScope and
-    // DidStartServiceWorkerContext).
-    kReady,
-  };
-
-  // Render process worker state of an activated extension.
-  enum class RendererState {
-    // Worker thread has not started or has been stopped/terminated.
-    kNotActive,
-    // Worker thread has started and it's running.
-    kActive,
-  };
-
   // Convenience method to return the ServiceWorkerTaskQueue for a given
   // `context`.
   static ServiceWorkerTaskQueue* Get(content::BrowserContext* context);
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.h b/gpu/ipc/client/command_buffer_proxy_impl.h
index 0a072a0..56c09d1 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl.h
+++ b/gpu/ipc/client/command_buffer_proxy_impl.h
@@ -10,7 +10,6 @@
 
 #include <map>
 #include <memory>
-#include <queue>
 #include <string>
 #include <unordered_map>
 #include <utility>
diff --git a/infra/config/generated/builder-owners/chrome-blink-engprod@google.com.txt b/infra/config/generated/builder-owners/chrome-blink-engprod@google.com.txt
index a48a9270..af22ab5 100644
--- a/infra/config/generated/builder-owners/chrome-blink-engprod@google.com.txt
+++ b/infra/config/generated/builder-owners/chrome-blink-engprod@google.com.txt
@@ -1,12 +1,10 @@
 ci/android-chrome-13-x64-wpt-android-specific
-ci/android-webview-13-x64-wpt-android-specific
 ci/blink-flake-suppressor
 ci/blink-web-test-analyzer
 ci/linux-wpt-chromium-rel
 ci/mac13-wpt-chromium-rel
 ci/win10-wpt-chromium-rel
 try/android-chrome-13-x64-wpt-android-specific
-try/android-webview-13-x64-wpt-android-specific
 try/linux-blink-rel
 try/linux-wpt-chromium-rel
 try/mac11.0-blink-rel
diff --git a/infra/config/generated/builders/ci/Dawn Win10 x64 Builder/targets/chromium.dawn.json b/infra/config/generated/builders/ci/Dawn Win10 x64 Builder/targets/chromium.dawn.json
index caf3d10..0adbab5 100644
--- a/infra/config/generated/builders/ci/Dawn Win10 x64 Builder/targets/chromium.dawn.json
+++ b/infra/config/generated/builders/ci/Dawn Win10 x64 Builder/targets/chromium.dawn.json
@@ -37,23 +37,353 @@
     ]
   },
   "Dawn Win10 x64 Experimental Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--enable-implicit-device-sync",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_implicit_device_sync_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--disable-toggles=use_dxc",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_no_dxc_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=use_tint_ir",
+          "--disable-toggles=use_dxc",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_no_dxc_use_tint_ir_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--disable-toggles=use_dxc",
+          "--enable-backend-validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_no_dxc_validation_layers_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=skip_validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_skip_validation_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=use_tint_ir",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_use_tint_ir_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-backend-validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-wire",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_wire_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_unittests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
+          "--override-steps=1",
+          "--gtest-benchmark-name=dawn_perf_tests",
+          "-v"
         ],
         "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "dawn_perf_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_perf_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_perf_tests/"
+      },
+      {
+        "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "telemetry_gpu_unittests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "dimensions": {
             "display_attached": "1",
@@ -67,6 +397,481 @@
           "io_timeout": 1800,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "telemetry_gpu_unittests",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_unittests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-worker=dedicated",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_dedicated_worker_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--enable-dawn-backend-validation",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-worker=service",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_service_worker_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-worker=shared",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_shared_worker_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--enable-dawn-backend-validation",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--enable-dawn-backend-validation",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
diff --git "a/infra/config/generated/builders/ci/Dawn Win10 x64 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json" "b/infra/config/generated/builders/ci/Dawn Win10 x64 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
index 9666298..a26b20a 100644
--- "a/infra/config/generated/builders/ci/Dawn Win10 x64 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
+++ "b/infra/config/generated/builders/ci/Dawn Win10 x64 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
@@ -1,22 +1,352 @@
 {
   "Dawn Win10 x64 Experimental Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--enable-implicit-device-sync",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_implicit_device_sync_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--disable-toggles=use_dxc",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_no_dxc_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=use_tint_ir",
+          "--disable-toggles=use_dxc",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_no_dxc_use_tint_ir_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--disable-toggles=use_dxc",
+          "--enable-backend-validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_no_dxc_validation_layers_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=skip_validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_skip_validation_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=use_tint_ir",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_use_tint_ir_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-backend-validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-wire",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_wire_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_unittests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
+          "--override-steps=1",
+          "--gtest-benchmark-name=dawn_perf_tests",
+          "-v"
         ],
         "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "dawn_perf_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_perf_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_perf_tests/"
+      },
+      {
+        "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "telemetry_gpu_unittests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "dimensions": {
             "display_attached": "1",
@@ -30,6 +360,481 @@
           "io_timeout": 1800,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "telemetry_gpu_unittests",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_unittests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-worker=dedicated",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_dedicated_worker_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--enable-dawn-backend-validation",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-worker=service",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_service_worker_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-worker=shared",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_shared_worker_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--enable-dawn-backend-validation",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--enable-dawn-backend-validation",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
diff --git a/infra/config/generated/builders/ci/Dawn Win10 x86 Builder/targets/chromium.dawn.json b/infra/config/generated/builders/ci/Dawn Win10 x86 Builder/targets/chromium.dawn.json
index 7d51189..b1e5055 100644
--- a/infra/config/generated/builders/ci/Dawn Win10 x86 Builder/targets/chromium.dawn.json
+++ b/infra/config/generated/builders/ci/Dawn Win10 x86 Builder/targets/chromium.dawn.json
@@ -37,23 +37,241 @@
     ]
   },
   "Dawn Win10 x86 Experimental Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--enable-implicit-device-sync",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_implicit_device_sync_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=skip_validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_skip_validation_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-backend-validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-wire",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_wire_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_unittests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
+          "--override-steps=1",
+          "--gtest-benchmark-name=dawn_perf_tests",
+          "-v"
         ],
         "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "dawn_perf_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_perf_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_perf_tests/"
+      },
+      {
+        "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "telemetry_gpu_unittests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "dimensions": {
             "display_attached": "1",
@@ -67,6 +285,290 @@
           "io_timeout": 1800,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "telemetry_gpu_unittests",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_unittests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--enable-dawn-backend-validation",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--enable-dawn-backend-validation",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
diff --git "a/infra/config/generated/builders/ci/Dawn Win10 x86 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json" "b/infra/config/generated/builders/ci/Dawn Win10 x86 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
index 054a68fd..caf16ee 100644
--- "a/infra/config/generated/builders/ci/Dawn Win10 x86 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
+++ "b/infra/config/generated/builders/ci/Dawn Win10 x86 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
@@ -1,22 +1,240 @@
 {
   "Dawn Win10 x86 Experimental Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--enable-implicit-device-sync",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_implicit_device_sync_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=skip_validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_skip_validation_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-backend-validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-wire",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_wire_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_unittests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
+          "--override-steps=1",
+          "--gtest-benchmark-name=dawn_perf_tests",
+          "-v"
         ],
         "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "dawn_perf_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_perf_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_perf_tests/"
+      },
+      {
+        "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "telemetry_gpu_unittests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "dimensions": {
             "display_attached": "1",
@@ -30,6 +248,290 @@
           "io_timeout": 1800,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "telemetry_gpu_unittests",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_unittests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--enable-dawn-backend-validation",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--enable-dawn-backend-validation",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
diff --git a/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json b/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
index f9f9a36..a79d838 100644
--- a/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
+++ b/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
@@ -1436,23 +1436,6 @@
         "test_id_prefix": "ninja://storage:storage_unittests/"
       },
       {
-        "isolate_profile_data": true,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "sync_integration_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Windows-10-19045"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "sync_integration_tests",
-        "test_id_prefix": "ninja://chrome/test:sync_integration_tests/"
-      },
-      {
         "args": [
           "--disable-field-trial-config"
         ],
diff --git a/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json b/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
index d34bb54..e972f35 100644
--- a/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
+++ b/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
@@ -1412,23 +1412,6 @@
         "test_id_prefix": "ninja://storage:storage_unittests/"
       },
       {
-        "isolate_profile_data": true,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "sync_integration_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Windows-10-19045"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "sync_integration_tests",
-        "test_id_prefix": "ninja://chrome/test:sync_integration_tests/"
-      },
-      {
         "args": [
           "--disable-field-trial-config"
         ],
diff --git a/infra/config/generated/builders/ci/android-13-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/ci/android-13-x64-rel/targets/chromium.android.json
index 97c756e9..4b43f5e9 100644
--- a/infra/config/generated/builders/ci/android-13-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/ci/android-13-x64-rel/targets/chromium.android.json
@@ -3643,7 +3643,9 @@
           "always",
           "--avd-config=../../tools/android/avd/proto/android_33_google_apis_x64.textpb"
         ],
+        "ci_only": true,
         "description": "Run with android_33_google_apis_x64",
+        "experiment_percentage": 100,
         "merge": {
           "args": [
             "--verbose"
diff --git a/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json
index 729d0e2..b2c81b2 100644
--- a/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json
@@ -3475,7 +3475,9 @@
           "always",
           "--avd-config=../../tools/android/avd/proto/android_35_google_apis_x64.textpb"
         ],
+        "ci_only": true,
         "description": "Run with android_35_google_apis_x64",
+        "experiment_percentage": 100,
         "merge": {
           "args": [
             "--verbose"
diff --git a/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/gn-args.json b/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/gn-args.json
deleted file mode 100644
index 382c163..0000000
--- a/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/gn-args.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "gn_args": {
-    "android_static_analysis": "off",
-    "dcheck_always_on": false,
-    "debuggable_apks": false,
-    "ffmpeg_branding": "Chrome",
-    "is_component_build": false,
-    "is_debug": false,
-    "proprietary_codecs": true,
-    "strip_debug_info": true,
-    "symbol_level": 1,
-    "system_webview_package_name": "com.google.android.webview.debug",
-    "system_webview_shell_package_name": "org.chromium.my_webview_shell",
-    "target_cpu": "x64",
-    "target_os": "android",
-    "use_reclient": false,
-    "use_remoteexec": true,
-    "use_siso": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/properties.json b/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/properties.json
deleted file mode 100644
index aab9c3b..0000000
--- a/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/properties.json
+++ /dev/null
@@ -1,79 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "android-webview-13-x64-wpt-android-specific",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-android-archive",
-              "builder_group": "chromium.android.fyi",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_android_config": {
-                "config": "base_config"
-              },
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "main_builder",
-                "target_arch": "intel",
-                "target_bits": 64,
-                "target_platform": "android"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "android"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "android-webview-13-x64-wpt-android-specific",
-          "project": "chromium"
-        }
-      ],
-      "mirroring_builder_group_and_names": [
-        {
-          "builder": "android-webview-13-x64-wpt-android-specific",
-          "group": "tryserver.chromium.android"
-        }
-      ],
-      "targets_spec_directory": "src/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/targets"
-    }
-  },
-  "$build/siso": {
-    "configs": [
-      "builder"
-    ],
-    "enable_cloud_monitoring": true,
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "metrics_project": "chromium-reclient-metrics",
-    "project": "rbe-chromium-trusted",
-    "remote_jobs": 250
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.android.fyi",
-  "recipe": "chromium"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/shadow-properties.json b/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/shadow-properties.json
deleted file mode 100644
index 78dedff8..0000000
--- a/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/shadow-properties.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "$build/siso": {
-    "configs": [
-      "builder"
-    ],
-    "enable_cloud_monitoring": true,
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "metrics_project": "chromium-reclient-metrics",
-    "project": "rbe-chromium-untrusted",
-    "remote_jobs": 250
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/targets/chromium.android.fyi.json b/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/targets/chromium.android.fyi.json
deleted file mode 100644
index 6da6cc6..0000000
--- a/infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/targets/chromium.android.fyi.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
-  "android-webview-13-x64-wpt-android-specific": {
-    "isolated_scripts": [
-      {
-        "args": [
-          "--skipped",
-          "always",
-          "--avd-config=../../tools/android/avd/proto/android_33_google_apis_x64.textpb"
-        ],
-        "description": "Run with android_33_google_apis_x64",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "android_webview_wpt_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "results_handler": "layout tests",
-        "swarming": {
-          "dimensions": {
-            "cores": "8",
-            "cpu": "x86-64",
-            "device_os": null,
-            "device_type": null,
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests.avd"
-          },
-          "named_caches": [
-            {
-              "name": "android_33_google_apis_x64",
-              "path": ".android_emulator/android_33_google_apis_x64"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "android_33_google_apis_x64"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
-        },
-        "test": "trichrome_webview_wpt_64",
-        "test_id_prefix": "ninja://android_webview/test:trichrome_webview_wpt_64/"
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json
index 22642fc8..ee97ded 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -91,8 +91,7 @@
     "android-annotator-rel": "ci/android-annotator-rel/gn-args.json",
     "android-chrome-13-x64-wpt-android-specific": "ci/android-chrome-13-x64-wpt-android-specific/gn-args.json",
     "android-cronet-asan-x86-rel": "ci/android-cronet-asan-x86-rel/gn-args.json",
-    "android-pie-x86-fyi-rel": "ci/android-pie-x86-fyi-rel/gn-args.json",
-    "android-webview-13-x64-wpt-android-specific": "ci/android-webview-13-x64-wpt-android-specific/gn-args.json"
+    "android-pie-x86-fyi-rel": "ci/android-pie-x86-fyi-rel/gn-args.json"
   },
   "chromium.angle": {
     "android-angle-chromium-arm64-builder": "ci/android-angle-chromium-arm64-builder/gn-args.json",
@@ -643,7 +642,6 @@
     "android-webview-12-x64-dbg": "try/android-webview-12-x64-dbg/gn-args.json",
     "android-webview-13-x64-dbg": "try/android-webview-13-x64-dbg/gn-args.json",
     "android-webview-13-x64-hostside-rel": "try/android-webview-13-x64-hostside-rel/gn-args.json",
-    "android-webview-13-x64-wpt-android-specific": "try/android-webview-13-x64-wpt-android-specific/gn-args.json",
     "android-webview-oreo-arm64-dbg": "try/android-webview-oreo-arm64-dbg/gn-args.json",
     "android-webview-pie-arm64-dbg": "try/android-webview-pie-arm64-dbg/gn-args.json",
     "android-x64-rel": "try/android-x64-rel/gn-args.json",
diff --git a/infra/config/generated/builders/try/android-13-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/try/android-13-x64-rel/targets/chromium.android.json
index 97c756e9..4b43f5e9 100644
--- a/infra/config/generated/builders/try/android-13-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/try/android-13-x64-rel/targets/chromium.android.json
@@ -3643,7 +3643,9 @@
           "always",
           "--avd-config=../../tools/android/avd/proto/android_33_google_apis_x64.textpb"
         ],
+        "ci_only": true,
         "description": "Run with android_33_google_apis_x64",
+        "experiment_percentage": 100,
         "merge": {
           "args": [
             "--verbose"
diff --git a/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json
index 729d0e2..b2c81b2 100644
--- a/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json
@@ -3475,7 +3475,9 @@
           "always",
           "--avd-config=../../tools/android/avd/proto/android_35_google_apis_x64.textpb"
         ],
+        "ci_only": true,
         "description": "Run with android_35_google_apis_x64",
+        "experiment_percentage": 100,
         "merge": {
           "args": [
             "--verbose"
diff --git a/infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/gn-args.json b/infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/gn-args.json
deleted file mode 100644
index 00384776..0000000
--- a/infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/gn-args.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "gn_args": {
-    "android_static_analysis": "off",
-    "dcheck_always_on": true,
-    "debuggable_apks": false,
-    "ffmpeg_branding": "Chrome",
-    "is_component_build": false,
-    "is_debug": false,
-    "proprietary_codecs": true,
-    "strip_debug_info": true,
-    "symbol_level": 0,
-    "system_webview_package_name": "com.google.android.webview.debug",
-    "system_webview_shell_package_name": "org.chromium.my_webview_shell",
-    "target_cpu": "x64",
-    "target_os": "android",
-    "use_reclient": false,
-    "use_remoteexec": true,
-    "use_siso": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/properties.json b/infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/properties.json
deleted file mode 100644
index f76e7a7..0000000
--- a/infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/properties.json
+++ /dev/null
@@ -1,75 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "android-webview-13-x64-wpt-android-specific",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-android-archive",
-              "builder_group": "chromium.android.fyi",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_android_config": {
-                "config": "base_config"
-              },
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "main_builder",
-                "target_arch": "intel",
-                "target_bits": 64,
-                "target_platform": "android"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "android"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "android-webview-13-x64-wpt-android-specific",
-          "project": "chromium"
-        }
-      ],
-      "targets_spec_directory": "src/infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/targets"
-    }
-  },
-  "$build/siso": {
-    "configs": [
-      "builder",
-      "remote-link"
-    ],
-    "enable_cloud_monitoring": true,
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "metrics_project": "chromium-reclient-metrics",
-    "output_local_strategy": "greedy",
-    "project": "rbe-chromium-untrusted",
-    "remote_jobs": -1
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.android",
-  "recipe": "chromium_trybot"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/targets/chromium.android.fyi.json b/infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/targets/chromium.android.fyi.json
deleted file mode 100644
index 6da6cc6..0000000
--- a/infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/targets/chromium.android.fyi.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
-  "android-webview-13-x64-wpt-android-specific": {
-    "isolated_scripts": [
-      {
-        "args": [
-          "--skipped",
-          "always",
-          "--avd-config=../../tools/android/avd/proto/android_33_google_apis_x64.textpb"
-        ],
-        "description": "Run with android_33_google_apis_x64",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "android_webview_wpt_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "results_handler": "layout tests",
-        "swarming": {
-          "dimensions": {
-            "cores": "8",
-            "cpu": "x86-64",
-            "device_os": null,
-            "device_type": null,
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests.avd"
-          },
-          "named_caches": [
-            {
-              "name": "android_33_google_apis_x64",
-              "path": ".android_emulator/android_33_google_apis_x64"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "android_33_google_apis_x64"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
-        },
-        "test": "trichrome_webview_wpt_64",
-        "test_id_prefix": "ninja://android_webview/test:trichrome_webview_wpt_64/"
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json
index 816862d4..a0a1718 100644
--- a/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json
@@ -3532,7 +3532,9 @@
           "always",
           "--avd-config=../../tools/android/avd/proto/android_35_google_apis_x64.textpb"
         ],
+        "ci_only": true,
         "description": "Run with android_35_google_apis_x64",
+        "experiment_percentage": 100,
         "merge": {
           "args": [
             "--verbose"
diff --git a/infra/config/generated/builders/try/dawn-try-win-x64-nvidia-exp/targets/chromium.dawn.json b/infra/config/generated/builders/try/dawn-try-win-x64-nvidia-exp/targets/chromium.dawn.json
index e8ad0bc9..93284b3c 100644
--- a/infra/config/generated/builders/try/dawn-try-win-x64-nvidia-exp/targets/chromium.dawn.json
+++ b/infra/config/generated/builders/try/dawn-try-win-x64-nvidia-exp/targets/chromium.dawn.json
@@ -1,23 +1,353 @@
 {
   "Dawn Win10 x64 Builder": {},
   "Dawn Win10 x64 Experimental Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--enable-implicit-device-sync",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_implicit_device_sync_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--disable-toggles=use_dxc",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_no_dxc_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=use_tint_ir",
+          "--disable-toggles=use_dxc",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_no_dxc_use_tint_ir_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--disable-toggles=use_dxc",
+          "--enable-backend-validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_no_dxc_validation_layers_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=skip_validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_skip_validation_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=use_tint_ir",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_use_tint_ir_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-backend-validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-wire",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_wire_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_unittests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
+          "--override-steps=1",
+          "--gtest-benchmark-name=dawn_perf_tests",
+          "-v"
         ],
         "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "dawn_perf_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_perf_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_perf_tests/"
+      },
+      {
+        "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "telemetry_gpu_unittests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "dimensions": {
             "display_attached": "1",
@@ -31,6 +361,481 @@
           "io_timeout": 1800,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "telemetry_gpu_unittests",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_unittests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-worker=dedicated",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_dedicated_worker_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--enable-dawn-backend-validation",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-worker=service",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_service_worker_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-worker=shared",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_shared_worker_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--enable-dawn-backend-validation",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--enable-dawn-backend-validation",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
diff --git a/infra/config/generated/builders/try/dawn-try-win-x86-nvidia-exp/targets/chromium.dawn.json b/infra/config/generated/builders/try/dawn-try-win-x86-nvidia-exp/targets/chromium.dawn.json
index e4dbbe9..0bff4a2 100644
--- a/infra/config/generated/builders/try/dawn-try-win-x86-nvidia-exp/targets/chromium.dawn.json
+++ b/infra/config/generated/builders/try/dawn-try-win-x86-nvidia-exp/targets/chromium.dawn.json
@@ -1,23 +1,241 @@
 {
   "Dawn Win10 x86 Builder": {},
   "Dawn Win10 x86 Experimental Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--enable-implicit-device-sync",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_implicit_device_sync_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-toggles=skip_validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_skip_validation_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--enable-backend-validation",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_validation_layers_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-wire",
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "dawn_end2end_wire_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_unittests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
+          "--override-steps=1",
+          "--gtest-benchmark-name=dawn_perf_tests",
+          "-v"
         ],
         "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "dawn_perf_tests",
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_perf_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_perf_tests/"
+      },
+      {
+        "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "telemetry_gpu_unittests",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "dimensions": {
             "display_attached": "1",
@@ -31,6 +249,290 @@
           "io_timeout": 1800,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "telemetry_gpu_unittests",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_unittests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--enable-dawn-backend-validation",
+          "--use-fxc",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "ci_only": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_cts_fxc_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 14
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "--flag-specific=webgpu-swiftshader-with-backend-validation",
+          "--timeout-ms=30000",
+          "--initialize-webgpu-adapter-at-startup-timeout-ms=60000"
+        ],
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webgpu_swiftshader_blink_web_tests_with_backend_validation",
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "webgpu_blink_web_tests",
+        "test_id_prefix": "ninja://:webgpu_blink_web_tests/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--use-webgpu-adapter=swiftshader",
+          "--test-filter=*web_platform*",
+          "--enable-dawn-backend-validation",
+          "--jobs=4",
+          "--use-webgpu-power-preference=default-high-performance"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgpu_swiftshader_web_platform_cts_with_validation_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
diff --git a/infra/config/generated/builders/try/win-rel/targets/chromium.win.json b/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
index 8d94aaf..adff9d9 100644
--- a/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
+++ b/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
@@ -1436,23 +1436,6 @@
         "test_id_prefix": "ninja://storage:storage_unittests/"
       },
       {
-        "isolate_profile_data": true,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "sync_integration_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Windows-10-19045"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "sync_integration_tests",
-        "test_id_prefix": "ninja://chrome/test:sync_integration_tests/"
-      },
-      {
         "args": [
           "--disable-field-trial-config"
         ],
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json
index 519c588d..7ee9cf9 100644
--- a/infra/config/generated/health-specs/health-specs.json
+++ b/infra/config/generated/health-specs/health-specs.json
@@ -8333,27 +8333,6 @@
           }
         ]
       },
-      "android-webview-13-x64-wpt-android-specific": {
-        "contact_team_email": "chrome-blink-engprod@google.com",
-        "problem_specs": [
-          {
-            "name": "Unhealthy",
-            "period_days": 7,
-            "score": 5,
-            "thresholds": {
-              "_default": "_default"
-            }
-          },
-          {
-            "name": "Low Value",
-            "period_days": 90,
-            "score": 1,
-            "thresholds": {
-              "_default": "_default"
-            }
-          }
-        ]
-      },
       "android-webview-code-coverage": {
         "contact_team_email": "woa-engprod@google.com",
         "problem_specs": [
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index fd59fd3..421ba9c2 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -1991,11 +1991,6 @@
         disable_reuse_footers: "Include-Ci-Only-Tests"
       }
       builders {
-        name: "chromium/try/android-webview-13-x64-wpt-android-specific"
-        includable_only: true
-        disable_reuse_footers: "Include-Ci-Only-Tests"
-      }
-      builders {
         name: "chromium/try/android-webview-oreo-arm64-dbg"
         includable_only: true
         disable_reuse_footers: "Include-Ci-Only-Tests"
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 55f6788..9052b1c 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -47330,118 +47330,6 @@
       }
     }
     builders {
-      name: "android-webview-13-x64-wpt-android-specific"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/properties.json",'
-        '    "shadow_properties_file": "infra/config/generated/builders/ci/android-webview-13-x64-wpt-android-specific/shadow-properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.android.fyi",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      priority: 35
-      execution_timeout_secs: 10800
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium.use_per_builder_build_dir_name"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "Run wpt tests on Android Webview in Android 13 emulators.<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android-webview-13-x64-wpt-android-specific\">android-webview-13-x64-wpt-android-specific</a></li></ul><br/>Builder owner: <a href=mailto:chrome-blink-engprod@google.com>chrome-blink-engprod@google.com</a>"
-      shadow_builder_adjustments {
-        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-        pool: "luci.chromium.try"
-        dimensions: "free_space:"
-        dimensions: "pool:luci.chromium.try"
-      }
-      contact_team_email: "chrome-blink-engprod@google.com"
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/cached_count"
-        predicates: "has(build.output.properties.is_cached)"
-        predicates: "string(build.output.properties.is_cached) == \"true\""
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
-        predicates: "has(build.output.properties.ran_tests_retry_shard)"
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
-        predicates: "has(build.output.properties.ran_tests_without_patch)"
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/uncached_count"
-        predicates: "has(build.output.properties.is_cached)"
-        predicates: "string(build.output.properties.is_cached) == \"false\""
-      }
-    }
-    builders {
       name: "android-webview-code-coverage"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -91330,118 +91218,6 @@
       }
     }
     builders {
-      name: "android-webview-13-x64-wpt-android-specific"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/try/android-webview-13-x64-wpt-android-specific/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium.android",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium.use_per_builder_build_dir_name"
-        value: 100
-      }
-      experiments {
-        key: "luci.buildbucket.canary_software"
-        value: 5
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "<br>Run wpt tests on Android Webview in Android 13 emulators.<br/><br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/android-webview-13-x64-wpt-android-specific\">android-webview-13-x64-wpt-android-specific</a></li></ul><br/>Builder owner: <a href=mailto:chrome-blink-engprod@google.com>chrome-blink-engprod@google.com</a>"
-      contact_team_email: "chrome-blink-engprod@google.com"
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/cached_count"
-        predicates: "has(build.output.properties.is_cached)"
-        predicates: "string(build.output.properties.is_cached) == \"true\""
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
-        predicates: "has(build.output.properties.ran_tests_retry_shard)"
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
-        predicates: "has(build.output.properties.ran_tests_without_patch)"
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/uncached_count"
-        predicates: "has(build.output.properties.is_cached)"
-        predicates: "string(build.output.properties.is_cached) == \"false\""
-      }
-    }
-    builders {
       name: "android-webview-oreo-arm64-dbg"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 80e0da1..ffee3650 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -10161,11 +10161,6 @@
     short_name: "15"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/android-webview-13-x64-wpt-android-specific"
-    category: "wpt|webview"
-    short_name: "13-x64"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/android-15-webview-wpt-fyi-rel"
     category: "wpt|webview"
     short_name: "15"
@@ -13229,6 +13224,11 @@
     short_name: "asn"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Dawn Win10 x64 Experimental Release (NVIDIA)"
+    category: "ToT|Windows|x64|Nvidia"
+    short_name: "exp"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Dawn Win10 x86 Release (Intel)"
     category: "ToT|Windows|x86|Intel"
     short_name: "630"
@@ -13239,6 +13239,11 @@
     short_name: "1660"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Dawn Win10 x86 Experimental Release (NVIDIA)"
+    category: "ToT|Windows|x86|Nvidia"
+    short_name: "exp"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Dawn ChromeOS Skylab Release (volteer)"
     category: "ChromeOS|Intel"
     short_name: "vlt"
@@ -24915,6 +24920,11 @@
     short_name: "1660"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Dawn Win10 x64 Experimental Release (NVIDIA)"
+    category: "chromium.dawn|ToT|Windows|x64|Nvidia"
+    short_name: "exp"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Dawn Win10 x86 Release (Intel)"
     category: "chromium.dawn|ToT|Windows|x86|Intel"
     short_name: "630"
@@ -24924,6 +24934,11 @@
     category: "chromium.dawn|ToT|Windows|x86|Nvidia"
     short_name: "1660"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Dawn Win10 x86 Experimental Release (NVIDIA)"
+    category: "chromium.dawn|ToT|Windows|x86|Nvidia"
+    short_name: "exp"
+  }
   header {
     oncalls {
       name: "Chromium"
@@ -26769,9 +26784,6 @@
     name: "buildbucket/luci.chromium.try/android-webview-13-x64-hostside-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android-webview-13-x64-wpt-android-specific"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android-webview-oreo-arm64-dbg"
   }
   builders {
@@ -28292,9 +28304,6 @@
     name: "buildbucket/luci.chromium.try/android-webview-13-x64-hostside-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android-webview-13-x64-wpt-android-specific"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android-webview-oreo-arm64-dbg"
   }
   builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 58c5e33..3b5449d 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -4151,15 +4151,6 @@
   }
 }
 job {
-  id: "android-webview-13-x64-wpt-android-specific"
-  realm: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "android-webview-13-x64-wpt-android-specific"
-  }
-}
-job {
   id: "android-webview-code-coverage"
   realm: "ci"
   schedule: "triggered"
@@ -6879,7 +6870,6 @@
   triggers: "android-rust-arm64-dbg"
   triggers: "android-rust-arm64-rel"
   triggers: "android-webview-13-x64-hostside-rel"
-  triggers: "android-webview-13-x64-wpt-android-specific"
   triggers: "android-x86-rel"
   triggers: "chromeos-amd64-generic-asan-rel"
   triggers: "chromeos-amd64-generic-cfi-thin-lto-rel"
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
index 882e964..c928e64 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
@@ -157,62 +157,6 @@
 )
 
 ci.builder(
-    name = "android-webview-13-x64-wpt-android-specific",
-    description_html = "Run wpt tests on Android Webview in Android 13 emulators.",
-    builder_spec = builder_config.builder_spec(
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = ["android"],
-        ),
-        chromium_config = builder_config.chromium_config(
-            config = "main_builder",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-            target_arch = builder_config.target_arch.INTEL,
-            target_bits = 64,
-            target_platform = builder_config.target_platform.ANDROID,
-        ),
-        android_config = builder_config.android_config(
-            config = "base_config",
-        ),
-        build_gs_bucket = "chromium-android-archive",
-    ),
-    gn_args = gn_args.config(
-        configs = [
-            "android_builder",
-            "release_builder",
-            "remoteexec",
-            "minimal_symbols",
-            "x64",
-            "strip_debug_info",
-            "android_fastbuild",
-            "webview_trichrome",
-            "webview_shell",
-        ],
-    ),
-    targets = targets.bundle(
-        targets = [
-            "wpt_web_tests_webview",
-        ],
-        mixins = [
-            "has_native_resultdb_integration",
-            "13-x64-emulator",
-            "emulator-8-cores",
-            "linux-jammy",
-            "x86-64",
-        ],
-    ),
-    targets_settings = targets.settings(
-        os_type = targets.os_type.ANDROID,
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "wpt|webview",
-        short_name = "13-x64",
-    ),
-    contact_team_email = "chrome-blink-engprod@google.com",
-)
-
-ci.builder(
     name = "android-15-webview-wpt-fyi-rel",
     description_html = "This builder runs upstream web platform tests for reporting results to wpt.fyi.",
     builder_spec = builder_config.builder_spec(
diff --git a/infra/config/subprojects/chromium/ci/chromium.dawn.star b/infra/config/subprojects/chromium/ci/chromium.dawn.star
index a651f5a..f217fbe 100644
--- a/infra/config/subprojects/chromium/ci/chromium.dawn.star
+++ b/infra/config/subprojects/chromium/ci/chromium.dawn.star
@@ -2825,22 +2825,31 @@
         # should be running the same test_suites as
         # 'Dawn Win10 x64 Release (NVIDIA)'
         targets = [
-            "gpu_noop_sleep_telemetry_test",
+            "gpu_dawn_telemetry_win_x64_tests",
+            "gpu_dawn_integration_gtests_passthrough_win_x64",
+            "gpu_dawn_isolated_scripts",
         ],
         mixins = [
             "limited_capacity_bot",
             "win10_nvidia_gtx_1660_experimental",
         ],
+        per_test_modifications = {
+            "gl_unittests": targets.mixin(
+                args = [
+                    "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter",
+                ],
+            ),
+        },
     ),
     targets_settings = targets.settings(
         browser_config = targets.browser_config.RELEASE_X64,
         os_type = targets.os_type.WINDOWS,
     ),
     # Uncomment this entry when this experimental tester is actually in use.
-    # console_view_entry = consoles.console_view_entry(
-    #     category = "ToT|Windows|x64|Nvidia",
-    #     short_name = "exp",
-    # ),
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Windows|x64|Nvidia",
+        short_name = "exp",
+    ),
     list_view = "chromium.gpu.experimental",
     execution_timeout = 6 * time.hour,
 )
@@ -3169,22 +3178,31 @@
         # should be running the same test_suites as 'Dawn Win10 x86 Release
         # (NVIDIA)'.
         targets = [
-            "gpu_noop_sleep_telemetry_test",
+            "gpu_dawn_telemetry_tests_fxc",
+            "gpu_dawn_integration_gtests_passthrough",
+            "gpu_dawn_isolated_scripts",
         ],
         mixins = [
             "limited_capacity_bot",
             "win10_nvidia_gtx_1660_experimental",
         ],
+        per_test_modifications = {
+            "gl_unittests": targets.mixin(
+                args = [
+                    "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter",
+                ],
+            ),
+        },
     ),
     targets_settings = targets.settings(
         browser_config = targets.browser_config.RELEASE,
         os_type = targets.os_type.WINDOWS,
     ),
     # Uncomment this entry when this experimental tester is actually in use.
-    # console_view_entry = consoles.console_view_entry(
-    #     category = "ToT|Windows|x86|Nvidia",
-    #     short_name = "exp",
-    # ),
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Windows|x86|Nvidia",
+        short_name = "exp",
+    ),
     list_view = "chromium.gpu.experimental",
 )
 
diff --git a/infra/config/subprojects/chromium/ci/chromium.win.star b/infra/config/subprojects/chromium/ci/chromium.win.star
index fed9a9e..609dab83 100644
--- a/infra/config/subprojects/chromium/ci/chromium.win.star
+++ b/infra/config/subprojects/chromium/ci/chromium.win.star
@@ -461,10 +461,14 @@
                     shards = 4,
                 ),
             ),
-            "sync_integration_tests": targets.mixin(
-                swarming = targets.swarming(
-                    shards = 3,
-                ),
+            # TODO(crbug.com/419089901): Re-enable after the suite works.
+            #"sync_integration_tests": targets.mixin(
+            #    swarming = targets.swarming(
+            #        shards = 3,
+            #    ),
+            #),
+            "sync_integration_tests": targets.remove(
+                reason = "Having infra failure (crbug.com/419089901)",
             ),
             "telemetry_perf_unittests": targets.remove(
                 reason = "Some test cases fail on win-rel (crbug/40622135).",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index e815dce..9c906bb 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -1080,18 +1080,6 @@
 )
 
 try_.builder(
-    name = "android-webview-13-x64-wpt-android-specific",
-    mirrors = ["ci/android-webview-13-x64-wpt-android-specific"],
-    gn_args = gn_args.config(
-        configs = [
-            "ci/android-webview-13-x64-wpt-android-specific",
-            "release_try_builder",
-        ],
-    ),
-    contact_team_email = "chrome-blink-engprod@google.com",
-)
-
-try_.builder(
     name = "android-webview-12-x64-dbg",
     mirrors = [
         "ci/Android x64 Builder (dbg)",
diff --git a/infra/config/targets/bundles.star b/infra/config/targets/bundles.star
index 0243f555..615f5b57 100644
--- a/infra/config/targets/bundles.star
+++ b/infra/config/targets/bundles.star
@@ -806,9 +806,11 @@
             ),
         ),
         "android_webview_wpt_tests": targets.mixin(
+            ci_only = True,
             swarming = targets.swarming(
                 shards = 4,
             ),
+            experiment_percentage = 100,
         ),
         "android_webdriver_wpt_tests": targets.mixin(
             ci_only = True,
diff --git a/internal b/internal
index e5f2041..0e9afdc 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit e5f2041ac763622cc191728d4b6db0d67c86e284
+Subproject commit 0e9afdc3a302097ee9c1807dc20b8693f48e9ff2
diff --git a/ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h b/ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h
index 691cff9..1f0adcc 100644
--- a/ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h
+++ b/ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h
@@ -13,6 +13,11 @@
 // the UI does.
 const base::TimeDelta kSelectSuggestionDelay = base::Milliseconds(500);
 
+// The delay between showing the confirmation and dismissing the progress
+// dialog.
+const base::TimeDelta kProgressDialogConfirmationDismissDelay =
+    base::Seconds(1);
+
 }  // namespace autofill_ui_constants
 
 #endif  // IOS_CHROME_BROWSER_AUTOFILL_UI_BUNDLED_AUTOFILL_UI_CONSTANTS_H_
diff --git a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn
index fedcb52..0949bcd 100644
--- a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn
+++ b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn
@@ -165,6 +165,7 @@
     "//components/url_formatter",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/autofill/model:features",
+    "//ios/chrome/browser/autofill/ui_bundled:autofill_ui_constants",
     "//ios/chrome/browser/autofill/ui_bundled:eg_test_support+eg2",
     "//ios/chrome/browser/autofill/ui_bundled/authentication:card_unmask_authentication_selection_constants",
     "//ios/chrome/browser/autofill/ui_bundled/manual_fill:eg_test_support+eg2",
diff --git a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_egtest.mm b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_egtest.mm
index 90b3c859..cb72773f 100644
--- a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_egtest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_egtest.mm
@@ -16,6 +16,7 @@
 #import "components/url_formatter/elide_url.h"
 #import "ios/chrome/browser/autofill/model/features.h"
 #import "ios/chrome/browser/autofill/ui_bundled/autofill_app_interface.h"
+#import "ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h"
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_matchers.h"
 #import "ios/chrome/browser/metrics/model/metrics_app_interface.h"
 #import "ios/chrome/browser/settings/ui_bundled/settings_root_table_constants.h"
@@ -42,6 +43,11 @@
 const char kFormCardNumber[] = "CCNo";
 const char kFormCardExpirationMonth[] = "CCExpiresMonth";
 const char kFormCardExpirationYear[] = "CCExpiresYear";
+NSString* const kTriggeringRequestUrl =
+    @"https://payments.google.com/payments/apis-secure/creditcardservice/"
+    @"getrealpan?s7e_suffix=chromewallet";
+NSString* const kSuccessResponseNoAuthNeeded =
+    @"{ \"pan\": \"5411111111112109\" }";
 
 // Matcher for the credit card suggestion chip.
 id<GREYMatcher> KeyboardAccessoryCreditCardSuggestionChip() {
@@ -122,9 +128,6 @@
   } else if ([self isRunningTest:@selector
                    (testFillingFromKeyboardOnAutofocus_WithFix)]) {
     config.features_enabled.push_back(kAutofillFixPaymentSheetSpam);
-  } else if ([self isRunningTest:@selector
-                   (testFillingFromKeyboardOnAutofocus_WithoutFix)]) {
-    config.features_disabled.push_back(kAutofillFixPaymentSheetSpam);
   }
   return config;
 }
@@ -468,6 +471,8 @@
 
   id<GREYMatcher> continueButton = WaitOnResponsiveContinueButton();
 
+  [AutofillAppInterface setUpFakeCreditCardServer];
+
   // Add a credit card to the Personal Data Manager.
   id<GREYMatcher> serverCreditCardEntry =
       grey_text([AutofillAppInterface saveMaskedCreditCard]);
@@ -497,9 +502,26 @@
 
   [[EarlGrey selectElementWithMatcher:continueButton] performAction:grey_tap()];
 
-  // Verify the CVC requester is visible.
-  [[EarlGrey selectElementWithMatcher:grey_text(@"Verification")]
-      assertWithMatcher:grey_notNil()];
+  // Wait for the progress dialog to appear.
+  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:
+                      chrome_test_util::StaticTextWithAccessibilityLabelId(
+                          IDS_AUTOFILL_CARD_UNMASK_PROGRESS_DIALOG_TITLE)];
+  // Fake the successful server response that triggers Dismiss.
+  [AutofillAppInterface setPaymentsResponse:kSuccessResponseNoAuthNeeded
+                                 forRequest:kTriggeringRequestUrl
+                              withErrorCode:net::HTTP_OK];
+  // This delay is the autodismiss delay (1 second) + extra time to avoid
+  // flakiness on the simulators (2 seconds).
+  const base::TimeDelta total_delay_for_dismiss =
+      autofill_ui_constants::kProgressDialogConfirmationDismissDelay +
+      base::Seconds(2);
+
+  // Wait for the dialog to disappear after the delay.
+  [ChromeEarlGrey
+      waitForUIElementToDisappearWithMatcher:
+          chrome_test_util::StaticTextWithAccessibilityLabelId(
+              IDS_AUTOFILL_CARD_UNMASK_PROGRESS_DIALOG_TITLE)
+                                     timeout:total_delay_for_dismiss];
 
   GREYAssertNil(
       [MetricsAppInterface
@@ -509,9 +531,6 @@
                              @"Autofill.TouchToFill.CreditCard.SelectedIndex"],
       @"Unexpected histogram error for touch to fill credit card selected "
       @"index");
-
-  // TODO(crbug.com/40577448): Figure out a way to enter CVC and get the
-  // unlocked card result.
 }
 
 // Tests that accessing a long press menu does not disable the bottom sheet.
@@ -804,6 +823,7 @@
   // for this test case.
   [AutofillAppInterface clearCreditCardStore];
 
+  [AutofillAppInterface setUpFakeCreditCardServer];
   // Add the server credit card. Before loading the page so it can be in the
   // autofill suggestion upon autofocusing the credit card field.
   [AutofillAppInterface saveMaskedCreditCard];
@@ -846,54 +866,4 @@
       assertWithMatcher:grey_nil()];
 }
 
-// Tests that the payment sheet spams after filling from the KA on an
-// autofocused field, when the fix is disabled. This is a sanity check to make
-// sure that the test setup is right for testing that the fix really works (and
-// it doesn't only work because the tested case can't be reproed correctly).
-- (void)testFillingFromKeyboardOnAutofocus_WithoutFix {
-  if (!@available(iOS 18, *)) {
-    EARL_GREY_TEST_SKIPPED(@"The issue tested here started on ios18");
-  }
-
-  // Clear the credit cards to remove the default local cards that aren't needed
-  // for this test case.
-  [AutofillAppInterface clearCreditCardStore];
-
-  // Add the server credit card. Before loading the page so it can be in the
-  // autofill suggestion upon autofocusing the credit card field.
-  [AutofillAppInterface saveMaskedCreditCard];
-
-  // Load page for testing autofocus.
-  [self loadPaymentsWithAutofocusPage];
-
-  // Create the payment form dynamically with a field programmatically
-  // focused right after creation which will emulate an autofocus from the
-  // perspective of the bottom sheet (because the element will be already
-  // focused when the form is detected by Autofill which is when the sheet
-  // listeners are attached). The keyboard will automatically pop up.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
-      performAction:chrome_test_util::TapWebElementWithId("create-form-btn")];
-
-  // Wait for the keyboard accessory to appear.
-  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:
-                      manual_fill::FormSuggestionViewMatcher()];
-
-  // Tap on the card chip in the KA.
-  id<GREYMatcher> serverCardChip = KeyboardAccessoryCreditCardSuggestionChip();
-  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:serverCardChip];
-  [[EarlGrey selectElementWithMatcher:serverCardChip] performAction:grey_tap()];
-
-  // Tap on the "Cancel" button on the card unmask dialog to dismiss the dialog.
-  id<GREYMatcher> cancelBtnMatcher =
-      grey_allOf(grey_buttonTitle(l10n_util::GetNSString(IDS_CANCEL)),
-                 grey_sufficientlyVisible(), nil);
-  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:cancelBtnMatcher];
-  [[EarlGrey selectElementWithMatcher:cancelBtnMatcher]
-      performAction:grey_tap()];
-
-  // Verify that the sheet popped up after filling from the KA on the
-  // autofocused field.
-  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:ContinueButton()];
-}
-
 @end
diff --git a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/BUILD.gn b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/BUILD.gn
index 3faac1e..499e33d 100644
--- a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/BUILD.gn
+++ b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/BUILD.gn
@@ -16,6 +16,7 @@
     "//components/strings",
     "//ios/chrome/browser/alert_view/ui_bundled",
     "//ios/chrome/browser/autofill/model:model_internal",
+    "//ios/chrome/browser/autofill/ui_bundled:autofill_ui_constants",
     "//ios/chrome/browser/autofill/ui_bundled:coordinator",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/browser",
diff --git a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_egtest.mm b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_egtest.mm
index c598ab7..c7de6a4 100644
--- a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_egtest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_egtest.mm
@@ -36,13 +36,6 @@
 
 }  // namespace
 
-// Matcher for the "Cancel" button.
-id<GREYMatcher> CancelButton() {
-  return grey_allOf(
-      chrome_test_util::CancelButton(),
-      grey_not(grey_accessibilityTrait(UIAccessibilityTraitNotEnabled)), nil);
-}
-
 @interface AutofillProgressDialogDismissEGTest : ChromeTestCase {
   NSString* _enrolledCardNameAndLastFour;
 }
@@ -126,13 +119,18 @@
                                  forRequest:kTriggeringRequestUrl
                               withErrorCode:net::HTTP_OK];
 
-  // Wait for the dialog to disappear after the delay. This delay is the
-  // autodismiss delay (1 second) + extra time to avoid flakiness on the
-  // simulators (2 seconds).
-  [ChromeEarlGrey waitForUIElementToDisappearWithMatcher:
-                      chrome_test_util::StaticTextWithAccessibilityLabelId(
-                          IDS_AUTOFILL_CARD_UNMASK_PROGRESS_DIALOG_TITLE)
-                                                 timeout:base::Seconds(3)];
+  // This delay is the autodismiss delay (1 second) + extra time to avoid
+  // flakiness on the simulators (2 seconds).
+  const base::TimeDelta total_delay_for_dismiss =
+      autofill_ui_constants::kProgressDialogConfirmationDismissDelay +
+      base::Seconds(2);
+
+  // Wait for the dialog to disappear after the delay.
+  [ChromeEarlGrey
+      waitForUIElementToDisappearWithMatcher:
+          chrome_test_util::StaticTextWithAccessibilityLabelId(
+              IDS_AUTOFILL_CARD_UNMASK_PROGRESS_DIALOG_TITLE)
+                                     timeout:total_delay_for_dismiss];
 }
 
 @end
diff --git a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_mediator.mm b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_mediator.mm
index 370938b..d243e45d 100644
--- a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_mediator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_mediator.mm
@@ -17,16 +17,11 @@
 #import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/alert_view/ui_bundled/alert_action.h"
 #import "ios/chrome/browser/alert_view/ui_bundled/alert_consumer.h"
+#import "ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h"
 #import "ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_mediator_delegate.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ui/base/l10n/l10n_util.h"
 
-namespace {
-// The delay between showing the confirmation and dismissing the progress
-// dialog.
-constexpr base::TimeDelta kConfirmationDismissDelay = base::Seconds(1);
-}  // namespace
-
 AutofillProgressDialogMediator::AutofillProgressDialogMediator(
     autofill::AutofillProgressDialogControllerImpl* model_controller,
     id<AutofillProgressDialogMediatorDelegate> delegate)
@@ -64,7 +59,8 @@
                      weak_ptr_factory_.GetWeakPtr());
 
   base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
-      FROM_HERE, std::move(closure), kConfirmationDismissDelay);
+      FROM_HERE, std::move(closure),
+      autofill_ui_constants::kProgressDialogConfirmationDismissDelay);
 }
 
 void AutofillProgressDialogMediator::InvalidateControllerForCallbacks() {
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_constants.h b/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_constants.h
index 0f643aa..fafacb6 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_constants.h
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_constants.h
@@ -40,8 +40,8 @@
   kTipsWithProductImage = 16,
   kTips = 17,
   kSendTabPromo = 18,
-  kSetUpListDocking = 19,
-  kSetUpListAddressBar = 20,
+  // Removed: kSetUpListDocking = 19,
+  // Removed: kSetUpListAddressBar = 20,
   kShopCard = 21,
   kMaxValue = kShopCard,
 };
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_constants.mm b/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_constants.mm
index d067413..82a4fef 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_constants.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_constants.mm
@@ -73,8 +73,6 @@
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
     case ContentSuggestionsModuleType::kSetUpListAllSet:
       return true;
@@ -98,8 +96,6 @@
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
     case ContentSuggestionsModuleType::kSendTabPromo:
     case ContentSuggestionsModuleType::kSetUpListAllSet:
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_coordinator.mm b/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_coordinator.mm
index 82ec680..729622c 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_coordinator.mm
@@ -837,8 +837,6 @@
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
       [_setUpListMediator disableModule];
       break;
@@ -938,8 +936,6 @@
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
     case ContentSuggestionsModuleType::kSetUpListAllSet:
       return NotificationOptInAccessPoint::kSetUpList;
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_metrics_recorder.mm b/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_metrics_recorder.mm
index a921e35e..096badb 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_metrics_recorder.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_metrics_recorder.mm
@@ -94,8 +94,6 @@
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
     case ContentSuggestionsModuleType::kSetUpListAllSet:
       UMA_HISTOGRAM_EXACT_LINEAR(
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_collection_view.mm b/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_collection_view.mm
index 78dd7fb..4a7feff 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_collection_view.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_collection_view.mm
@@ -505,8 +505,6 @@
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
     case ContentSuggestionsModuleType::kSetUpListAllSet:
     case ContentSuggestionsModuleType::kShopCard:
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_context_menu_interaction_handler.mm b/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_context_menu_interaction_handler.mm
index 2045c7e..4c3a524 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_context_menu_interaction_handler.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_context_menu_interaction_handler.mm
@@ -35,8 +35,6 @@
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
     case ContentSuggestionsModuleType::kParcelTracking:
     case ContentSuggestionsModuleType::kPriceTrackingPromo:
@@ -137,8 +135,6 @@
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
       return l10n_util::GetNSStringF(
           IDS_IOS_SET_UP_LIST_HIDE_MODULE_CONTEXT_MENU_DESCRIPTION,
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_module_container.mm b/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_module_container.mm
index e198480..81d2f79 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_module_container.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_module_container.mm
@@ -402,8 +402,6 @@
     case ContentSuggestionsModuleType::kSetUpListSync:
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
     case ContentSuggestionsModuleType::kSetUpListAllSet:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
@@ -591,8 +589,6 @@
     case ContentSuggestionsModuleType::kSetUpListAutofill:
     case ContentSuggestionsModuleType::kSetUpListAllSet:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kSafetyCheck:
     case ContentSuggestionsModuleType::kTips:
       return YES;
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_module_contents_factory.mm b/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_module_contents_factory.mm
index 92d9680..dad5e61 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_module_contents_factory.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_module_contents_factory.mm
@@ -93,8 +93,6 @@
     case ContentSuggestionsModuleType::kSetUpListSync:
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
     case ContentSuggestionsModuleType::kSetUpListAllSet:
     case ContentSuggestionsModuleType::kSetUpListNotifications: {
diff --git a/ios/chrome/browser/ntp/shared/metrics/home_metrics.mm b/ios/chrome/browser/ntp/shared/metrics/home_metrics.mm
index 5b223aa..8fa3608 100644
--- a/ios/chrome/browser/ntp/shared/metrics/home_metrics.mm
+++ b/ios/chrome/browser/ntp/shared/metrics/home_metrics.mm
@@ -191,8 +191,6 @@
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
     case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kSetUpListDocking:
-    case ContentSuggestionsModuleType::kSetUpListAddressBar:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
     case ContentSuggestionsModuleType::kSetUpListAllSet:
     case ContentSuggestionsModuleType::kPlaceholder:
diff --git a/ios/chrome/browser/popup_menu/ui_bundled/DEPS b/ios/chrome/browser/popup_menu/ui_bundled/DEPS
index 226c83e..3b015fc 100644
--- a/ios/chrome/browser/popup_menu/ui_bundled/DEPS
+++ b/ios/chrome/browser/popup_menu/ui_bundled/DEPS
@@ -27,6 +27,7 @@
   "+ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h",
   "+ios/chrome/browser/bubble/model/tab_based_iph_browser_agent.h",
   "+ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h",
+  "+ios/chrome/browser/lens_overlay/model/lens_overlay_tab_helper.h",
   "+ios/chrome/browser/overlays/model/public/overlay_presenter.h",
   "+ios/chrome/browser/overlays/model/public/overlay_presenter_observer_bridge.h",
   "+ios/chrome/browser/overlays/model/public/overlay_request.h",
diff --git a/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/BUILD.gn b/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/BUILD.gn
index 1d0fc6d..8f384a25 100644
--- a/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/BUILD.gn
+++ b/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/BUILD.gn
@@ -59,6 +59,7 @@
     "//ios/chrome/browser/intelligence/features",
     "//ios/chrome/browser/intents/model:model_donation_helper",
     "//ios/chrome/browser/lens_overlay/coordinator:lens_overlay_availability",
+    "//ios/chrome/browser/lens_overlay/model",
     "//ios/chrome/browser/ntp/shared/metrics",
     "//ios/chrome/browser/overlays/model",
     "//ios/chrome/browser/policy/model",
diff --git a/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/overflow_menu_mediator.mm b/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/overflow_menu_mediator.mm
index 1fe382d..68e2e18 100644
--- a/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/overflow_menu_mediator.mm
+++ b/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/overflow_menu_mediator.mm
@@ -44,6 +44,7 @@
 #import "ios/chrome/browser/intelligence/features/features.h"
 #import "ios/chrome/browser/intents/model/intents_donation_helper.h"
 #import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h"
+#import "ios/chrome/browser/lens_overlay/model/lens_overlay_tab_helper.h"
 #import "ios/chrome/browser/ntp/shared/metrics/feed_metrics_recorder.h"
 #import "ios/chrome/browser/overlays/model/public/overlay_presenter.h"
 #import "ios/chrome/browser/overlays/model/public/overlay_presenter_observer_bridge.h"
@@ -1481,6 +1482,15 @@
   if (IsReaderModeAvailable()) {
     self.readerModeAction.enabled = [self isReaderModeEnabled];
   }
+
+  // If Lens Overlay is already being displayed, disable the action.
+  if (self.webState) {
+    if (LensOverlayTabHelper* lensOverlayTabHelper =
+            LensOverlayTabHelper::FromWebState(self.webState)) {
+      self.lensOverlayAction.enabled =
+          !lensOverlayTabHelper->IsLensOverlayUIAttachedAndAlive();
+    }
+  }
 }
 
 // Updates the order of the items in each section or group.
diff --git a/ios/chrome/share_extension/extended_share_view_controller.mm b/ios/chrome/share_extension/extended_share_view_controller.mm
index c423587b..917359d1 100644
--- a/ios/chrome/share_extension/extended_share_view_controller.mm
+++ b/ios/chrome/share_extension/extended_share_view_controller.mm
@@ -94,10 +94,10 @@
 
 - (void)viewDidLoad {
   [super viewDidLoad];
-
+  self.view.backgroundColor = [UIColor clearColor];
   self.shareSheet = [[ShareExtensionSheet alloc] init];
   self.shareSheet.delegate = self;
-  self.shareSheet.modalPresentationStyle = UIModalPresentationPageSheet;
+  self.shareSheet.modalPresentationStyle = UIModalPresentationFormSheet;
   UISheetPresentationController* presentationController =
       self.shareSheet.sheetPresentationController;
   presentationController.prefersEdgeAttachedInCompactHeight = YES;
@@ -252,23 +252,13 @@
   }
 }
 
-- (void)shareExtensionSheetWillDisappear:
+- (void)shareExtensionSheetDidDisappear:
     (ShareExtensionSheet*)shareExtensionSheet {
-  __weak ExtendedShareViewController* weakSelf = self;
   [self
-      queueActionItemURL:nil
-                   title:nil
-                  action:app_group::READING_LIST_ITEM  // Ignored
-                  cancel:YES
-              completion:^{
-                [weakSelf
-                    dismissAndReturnItem:nil
-                                   error:
-                                       [NSError
-                                           errorWithDomain:NSCocoaErrorDomain
+      handleSheetDismissalForItem:nil
+                            error:[NSError errorWithDomain:NSCocoaErrorDomain
                                                       code:NSUserCancelledError
                                                   userInfo:nil]];
-              }];
 }
 
 #pragma mark - Private methods
diff --git a/ios/chrome/share_extension/share_extension_delegate.h b/ios/chrome/share_extension/share_extension_delegate.h
index 8526f241..3838735 100644
--- a/ios/chrome/share_extension/share_extension_delegate.h
+++ b/ios/chrome/share_extension/share_extension_delegate.h
@@ -22,7 +22,7 @@
     (ShareExtensionSheet*)shareExtensionSheet;
 - (void)didTapSearchInIncognitoShareExtensionSheet:
     (ShareExtensionSheet*)shareExtensionSheet;
-- (void)shareExtensionSheetWillDisappear:
+- (void)shareExtensionSheetDidDisappear:
     (ShareExtensionSheet*)shareExtensionSheet;
 
 @end
diff --git a/ios/chrome/share_extension/share_extension_sheet.mm b/ios/chrome/share_extension/share_extension_sheet.mm
index ce0dec6..d1e57cb8 100644
--- a/ios/chrome/share_extension/share_extension_sheet.mm
+++ b/ios/chrome/share_extension/share_extension_sheet.mm
@@ -88,10 +88,12 @@
   [self setUpBottomSheetDetents];
 }
 
-- (void)viewWillDisappear:(BOOL)animated {
-  [super viewWillDisappear:animated];
-  if (!self.dismissedFromSheetAction) {
-    [self.delegate shareExtensionSheetWillDisappear:self];
+- (void)viewDidDisappear:(BOOL)animated {
+  [super viewDidDisappear:animated];
+  if (self.isBeingDismissed) {
+    if (!self.dismissedFromSheetAction) {
+      [self.delegate shareExtensionSheetDidDisappear:self];
+    }
   }
 }
 
@@ -189,7 +191,7 @@
 
 // Configures the bottom sheet's presentation controller appearance.
 - (void)setUpBottomSheetPresentationController {
-  self.modalPresentationStyle = UIModalPresentationPageSheet;
+  self.modalPresentationStyle = UIModalPresentationFormSheet;
   UISheetPresentationController* presentationController =
       self.sheetPresentationController;
   presentationController.prefersEdgeAttachedInCompactHeight = YES;
diff --git a/ios/web_view/internal/cwv_omnibox_input.mm b/ios/web_view/internal/cwv_omnibox_input.mm
index e64130d4..9dfd13e 100644
--- a/ios/web_view/internal/cwv_omnibox_input.mm
+++ b/ios/web_view/internal/cwv_omnibox_input.mm
@@ -65,7 +65,8 @@
     return;
   }
 
-  CHECK([[CWVGlobalState sharedInstance] isStarted]);
+  DCHECK([[CWVGlobalState sharedInstance] isStarted]);
+  [[CWVGlobalState sharedInstance] start];
 }
 
 - (instancetype)initWithText:(NSString*)inputText
diff --git a/ios/web_view/internal/cwv_web_view.mm b/ios/web_view/internal/cwv_web_view.mm
index 702c5657..cd8e852 100644
--- a/ios/web_view/internal/cwv_web_view.mm
+++ b/ios/web_view/internal/cwv_web_view.mm
@@ -461,7 +461,8 @@
     return;
   }
 
-  CHECK([[CWVGlobalState sharedInstance] isStarted]);
+  DCHECK([[CWVGlobalState sharedInstance] isStarted]);
+  [[CWVGlobalState sharedInstance] start];
 }
 
 + (BOOL)chromeContextMenuEnabled {
diff --git a/ios/web_view/internal/cwv_web_view_configuration.mm b/ios/web_view/internal/cwv_web_view_configuration.mm
index 28308e54..6caea6cc 100644
--- a/ios/web_view/internal/cwv_web_view_configuration.mm
+++ b/ios/web_view/internal/cwv_web_view_configuration.mm
@@ -61,7 +61,8 @@
     return;
   }
 
-  CHECK([[CWVGlobalState sharedInstance] isStarted]);
+  DCHECK([[CWVGlobalState sharedInstance] isStarted]);
+  [[CWVGlobalState sharedInstance] start];
 
   ios_web_view::EnsureBrowserStateKeyedServiceFactoriesBuilt();
 
diff --git a/ios_internal b/ios_internal
index 5f6cd52..f5eedda 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 5f6cd5274af6da369855f92f45b3721d4707b7e4
+Subproject commit f5eedda01c1ec38b4693396a33e154b66b958def
diff --git a/media/base/media_types.cc b/media/base/media_types.cc
index 7a7f52a..8695cc9 100644
--- a/media/base/media_types.cc
+++ b/media/base/media_types.cc
@@ -37,9 +37,13 @@
     case VideoCodec::kMPEG2:
     case VideoCodec::kMPEG4:
       break;
+    case VideoCodec::kHEVC:
+      // According to https://www.iana.org/assignments/media-types/video/H265 we
+      // should infer a value of 93 (level 3.1) if we do not know the level.
+      level = 93;
+      break;
     case VideoCodec::kH264:
     case VideoCodec::kVP9:
-    case VideoCodec::kHEVC:
       // 10 is the level_idc for level 1.0.
       level = 10;
       break;
diff --git a/media/capture/video/chromeos/camera_app_device_impl.h b/media/capture/video/chromeos/camera_app_device_impl.h
index 723c8ce..aeb26af 100644
--- a/media/capture/video/chromeos/camera_app_device_impl.h
+++ b/media/capture/video/chromeos/camera_app_device_impl.h
@@ -6,7 +6,6 @@
 #define MEDIA_CAPTURE_VIDEO_CHROMEOS_CAMERA_APP_DEVICE_IMPL_H_
 
 #include <map>
-#include <queue>
 #include <string>
 #include <utility>
 #include <vector>
diff --git a/media/gpu/av1_builder.cc b/media/gpu/av1_builder.cc
index bc05cc7..0626547 100644
--- a/media/gpu/av1_builder.cc
+++ b/media/gpu/av1_builder.cc
@@ -97,7 +97,7 @@
     ret.WriteBool(pic_hdr.error_resilient_mode);
   }
   ret.WriteBool(pic_hdr.disable_cdf_update);
-  ret.WriteBool(false);  // Disable allow screen content tools.
+  ret.WriteBool(pic_hdr.allow_screen_content_tools);
   ret.WriteBool(false);  // Disable frame size override flag.
   if (seq_hdr.enable_order_hint) {
     ret.Write(pic_hdr.order_hint, seq_hdr.order_hint_bits_minus_1 + 1);
@@ -128,6 +128,9 @@
     ret.WriteBool(false);  // Motion not switchable.
   } else {
     ret.WriteBool(false);  // Render and frame size are the same.
+    if (pic_hdr.allow_screen_content_tools) {
+      ret.WriteBool(pic_hdr.allow_intrabc);
+    }
   }
   if (!pic_hdr.disable_cdf_update) {
     ret.WriteBool(pic_hdr.disable_frame_end_update_cdf);
@@ -174,26 +177,28 @@
     ret.WriteBool(false);  // No delta q present.
   }
 
-  // Pack loop filter parameters.
-  ret.Write(pic_hdr.filter_level.at(0), 6);
-  ret.Write(pic_hdr.filter_level.at(1), 6);
-  if (pic_hdr.filter_level.at(0) || pic_hdr.filter_level.at(1)) {
-    ret.Write(pic_hdr.filter_level_u, 6);
-    ret.Write(pic_hdr.filter_level_v, 6);
-  }
-  ret.Write(pic_hdr.sharpness_level, 3);
-  ret.WriteBool(pic_hdr.loop_filter_delta_enabled);
+  if (!pic_hdr.allow_intrabc) {
+    // Pack loop filter parameters.
+    ret.Write(pic_hdr.filter_level.at(0), 6);
+    ret.Write(pic_hdr.filter_level.at(1), 6);
+    if (pic_hdr.filter_level.at(0) || pic_hdr.filter_level.at(1)) {
+      ret.Write(pic_hdr.filter_level_u, 6);
+      ret.Write(pic_hdr.filter_level_v, 6);
+    }
+    ret.Write(pic_hdr.sharpness_level, 3);
+    ret.WriteBool(pic_hdr.loop_filter_delta_enabled);
 
-  // Pack CDEF parameters.
-  if (seq_hdr.enable_cdef) {
-    uint8_t num_planes = 3;  // mono_chrome not supported.
-    ret.Write(2, 2);         // Set CDEF damping minus 3 to 5 - 3.
-    ret.Write(3, 2);         // Set cdef_bits to 3.
-    for (size_t i = 0; i < (1 << num_planes); i++) {
-      ret.Write(pic_hdr.cdef_y_pri_strength.at(i), 4);
-      ret.Write(pic_hdr.cdef_y_sec_strength.at(i), 2);
-      ret.Write(pic_hdr.cdef_uv_pri_strength.at(i), 4);
-      ret.Write(pic_hdr.cdef_uv_sec_strength.at(i), 2);
+    // Pack CDEF parameters.
+    if (seq_hdr.enable_cdef) {
+      uint8_t num_planes = 3;  // mono_chrome not supported.
+      ret.Write(2, 2);         // Set CDEF damping minus 3 to 5 - 3.
+      ret.Write(3, 2);         // Set cdef_bits to 3.
+      for (size_t i = 0; i < (1 << num_planes); i++) {
+        ret.Write(pic_hdr.cdef_y_pri_strength.at(i), 4);
+        ret.Write(pic_hdr.cdef_y_sec_strength.at(i), 2);
+        ret.Write(pic_hdr.cdef_uv_pri_strength.at(i), 4);
+        ret.Write(pic_hdr.cdef_uv_sec_strength.at(i), 2);
+      }
     }
   }
   ret.WriteBool(true);  // TxMode TX_MODE_SELECT.
diff --git a/media/gpu/av1_builder.h b/media/gpu/av1_builder.h
index 5b834a9e..834f2e4 100644
--- a/media/gpu/av1_builder.h
+++ b/media/gpu/av1_builder.h
@@ -111,6 +111,8 @@
   std::array<std::array<uint32_t, 8 /*libgav1::kMaxSegments*/>,
              8 /*libgav1::kSegmentFeatureMax*/>
       feature_data;
+  bool allow_screen_content_tools;
+  bool allow_intrabc;
 };
 
 }  // namespace media
diff --git a/media/gpu/av1_builder_unittest.cc b/media/gpu/av1_builder_unittest.cc
index 234f306..f138423f 100644
--- a/media/gpu/av1_builder_unittest.cc
+++ b/media/gpu/av1_builder_unittest.cc
@@ -71,6 +71,8 @@
     }
     pic_hdr.reduced_tx_set = true;
     pic_hdr.segmentation_enabled = false;
+    pic_hdr.allow_screen_content_tools = false;
+    pic_hdr.allow_intrabc = false;
 
     return pic_hdr;
   }
diff --git a/media/gpu/vaapi/av1_vaapi_video_encoder_delegate.cc b/media/gpu/vaapi/av1_vaapi_video_encoder_delegate.cc
index f8789b9..cc97487 100644
--- a/media/gpu/vaapi/av1_vaapi_video_encoder_delegate.cc
+++ b/media/gpu/vaapi/av1_vaapi_video_encoder_delegate.cc
@@ -353,6 +353,9 @@
       pic_hdr.feature_mask[i] = pic_param.segments.feature_mask[i];
     }
   }
+  pic_hdr.allow_screen_content_tools =
+      pic_param.picture_flags.bits.palette_mode_enable;
+  pic_hdr.allow_intrabc = pic_param.picture_flags.bits.allow_intrabc;
   return pic_hdr;
 }
 
diff --git a/media/gpu/windows/d3d12_video_encode_av1_delegate.cc b/media/gpu/windows/d3d12_video_encode_av1_delegate.cc
index 7e26cd87..ce695c1d 100644
--- a/media/gpu/windows/d3d12_video_encode_av1_delegate.cc
+++ b/media/gpu/windows/d3d12_video_encode_av1_delegate.cc
@@ -73,6 +73,7 @@
 }
 
 AV1BitstreamBuilder::FrameHeader FillAV1BuilderFrameHeader(
+    const D3D12VideoEncodeAV1Delegate::PictureControlFlags& picture_ctrl,
     const D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_CODEC_DATA& pic_params) {
   AV1BitstreamBuilder::FrameHeader frame_header{};
   frame_header.frame_type =
@@ -116,11 +117,15 @@
 
   frame_header.reduced_tx_set = true;
   frame_header.segmentation_enabled = false;
+  frame_header.allow_screen_content_tools =
+      picture_ctrl.allow_screen_content_tools;
+  frame_header.allow_intrabc = picture_ctrl.allow_intrabc;
 
   return frame_header;
 }
 
 aom::AV1RateControlRtcConfig ConvertToRateControlConfig(
+    bool is_screen,
     const VideoBitrateAllocation& bitrate_allocation,
     const D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC& resolution,
     uint32_t frame_rate,
@@ -157,9 +162,53 @@
     base::span(rc_config.max_quantizers)[tid] = rc_config.max_quantizer;
     base::span(rc_config.min_quantizers)[tid] = rc_config.min_quantizer;
   }
+  rc_config.is_screen = is_screen;
   return rc_config;
 }
 
+EncoderStatus::Or<D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS> GetEnabledAV1Features(
+    bool is_screen,
+    const D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS supported_features) {
+  const std::array<D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS, 3> expected_flgs = {
+      D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING,
+      D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS,
+      D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET,
+  };
+  auto enabled_features = D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_NONE;
+  for (const auto feature : expected_flgs) {
+    if (!(supported_features & feature)) {
+      return {
+          EncoderStatus::Codes::kEncoderHardwareDriverError,
+          base::StringPrintf(" d3d12 driver doesn't support %x .", feature)};
+    }
+    enabled_features |= feature;
+  }
+
+  // Enable AV1 SCC tools for screen content encoding.
+  if (is_screen) {
+    auto scc_tools = D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING |
+                     D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY;
+    if (supported_features & scc_tools) {
+      enabled_features |= scc_tools;
+    }
+  }
+  return enabled_features;
+}
+
+D3D12VideoEncodeAV1Delegate::PictureControlFlags GetAV1PictureControl(
+    bool is_keyframe,
+    const D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS enabled_features) {
+  D3D12VideoEncodeAV1Delegate::PictureControlFlags picture_ctrl;
+  picture_ctrl.allow_screen_content_tools =
+      enabled_features & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING;
+  // D3D12 AV1 VEA only allow intra block copy for keyframes.
+  picture_ctrl.allow_intrabc =
+      is_keyframe ? enabled_features &
+                        D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY
+                  : false;
+  return picture_ctrl;
+}
+
 }  // namespace
 
 // static
@@ -234,7 +283,9 @@
            VideoCodec::kAV1);
   CHECK(!config.HasSpatialLayer());
   CHECK(!config.HasTemporalLayer());
-  CHECK_GE(max_num_ref_frames_, 1ull);
+  CHECK_EQ(max_num_ref_frames_, 1ull)
+      << "Currently D3D12VideoEncodeAV1Delegate only support 1 reference "
+         "frame.";
 
   if (config.bitrate.mode() != Bitrate::Mode::kConstant) {
     return {EncoderStatus::Codes::kEncoderUnsupportedConfig,
@@ -276,35 +327,25 @@
     return status;
   }
 
-  auto enabled_features = D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_NONE;
-  if (!(config_support_limit.SupportedFeatureFlags &
-        D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING)) {
-    return {EncoderStatus::Codes::kEncoderHardwareDriverError,
-            "d3d12 driver doesn't support CDEF filtering."};
+  is_screen_ = config.content_type ==
+               VideoEncodeAccelerator::Config::ContentType::kDisplay;
+  auto features_or_error = GetEnabledAV1Features(
+      is_screen_, config_support_limit.SupportedFeatureFlags);
+  if (!features_or_error.has_value()) {
+    return std::move(features_or_error).error();
   }
-  enabled_features |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING;
+  enabled_features_ = std::move(features_or_error).value();
+  DVLOG(3) << base::StringPrintf("Enabled d3d12 encoding feature : %x.",
+                                 enabled_features_);
 
-  if (!(config_support_limit.SupportedFeatureFlags &
-        D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS)) {
-    return {EncoderStatus::Codes::kEncoderHardwareDriverError,
-            "d3d12 driver doesn't support order hint tools."};
-  }
-  enabled_features |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS;
-
-  if (!(config_support_limit.SupportedFeatureFlags &
-        D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET)) {
-    return {EncoderStatus::Codes::kEncoderHardwareDriverError,
-            "d3d12 driver doesn't support reduced tx set."};
-  }
-  enabled_features |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_REDUCED_TX_SET;
   D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION codec_config = {
-      .FeatureFlags = enabled_features,
+      .FeatureFlags = enabled_features_,
       .OrderHintBitsMinus1 = kDefaultOrderHintBitsMinus1};
 
   framerate_ = config.framerate;
   bitrate_allocation_ = AllocateBitrateForDefaultEncoding(config);
   software_brc_ = aom::AV1RateControlRTC::Create(
-      ConvertToRateControlConfig(bitrate_allocation_, input_size_,
+      ConvertToRateControlConfig(is_screen_, bitrate_allocation_, input_size_,
                                  config.framerate, 1 /*num_temporal_layers_*/));
 
   CHECK(config.gop_length.has_value());
@@ -391,8 +432,8 @@
   bitrate_allocation.SetBitrate(0, 0, bitrate.target_bps());
   if (bitrate_allocation != bitrate_allocation_ || framerate != framerate_) {
     software_brc_->UpdateRateControl(
-        ConvertToRateControlConfig(bitrate_allocation, input_size_, framerate,
-                                   1 /*num_temporal_layers_*/));
+        ConvertToRateControlConfig(is_screen_, bitrate_allocation, input_size_,
+                                   framerate, 1 /*num_temporal_layers_*/));
 
     bitrate_allocation_ = bitrate_allocation;
     framerate_ = framerate;
@@ -402,19 +443,31 @@
 }
 
 void D3D12VideoEncodeAV1Delegate::FillPictureControlParams(
-    bool force_keyframe) {
+    const VideoEncoder::EncodeOptions& options) {
   CHECK(software_brc_);
 
+  base::span picture_params_span = UNSAFE_BUFFERS(base::span(
+      reinterpret_cast<uint8_t*>(&picture_params_), sizeof(picture_params_)));
+  std::ranges::fill(picture_params_span, 0);
   // Update picture index and determine if a keyframe is needed.
   if (++picture_id_ == static_cast<int>(gop_sequence_.InterFramePeriod) ||
-      force_keyframe) {
+      options.key_frame) {
     picture_id_ = 0;
   }
-  bool request_keyframe = force_keyframe | (picture_id_ == 0);
+  bool request_keyframe = picture_id_ == 0;
 
   picture_params_.PictureIndex = picture_id_;
+  picture_ctrl_ = GetAV1PictureControl(request_keyframe, enabled_features_);
   picture_params_.Flags =
       D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_REDUCED_TX_SET;
+  if (picture_ctrl_.allow_screen_content_tools) {
+    picture_params_.Flags |=
+        D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_PALETTE_ENCODING;
+  }
+  if (picture_ctrl_.allow_intrabc) {
+    picture_params_.Flags |=
+        D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ALLOW_INTRA_BLOCK_COPY;
+  }
   picture_params_.FrameType =
       request_keyframe ? D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME
                        : D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_INTER_FRAME;
@@ -423,7 +476,7 @@
   picture_params_.InterpolationFilter =
       D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_EIGHTTAP;
   picture_params_.TxMode = D3D12_VIDEO_ENCODER_AV1_TX_MODE_SELECT;
-  picture_params_.SuperResDenominator = 0;
+  picture_params_.SuperResDenominator = 8 /*SUPERRES_NUM*/;
   picture_params_.OrderHint =
       picture_params_.PictureIndex % (1 << (kDefaultOrderHintBitsMinus1 + 1));
   picture_params_.TemporalLayerIndexPlus1 = 0;
@@ -457,22 +510,26 @@
   DVLOG(4) << base::StringPrintf(
       "Encoding picture: %d, is_keyframe = %d, QP = %d", picture_id_,
       request_keyframe, computed_qp);
-  const aom::AV1LoopfilterLevel lf = software_brc_->GetLoopfilterLevel();
-  base::span(picture_params_.LoopFilter.LoopFilterLevel)[0] =
-      base::span(lf.filter_level)[0];
-  base::span(picture_params_.LoopFilter.LoopFilterLevel)[1] =
-      base::span(lf.filter_level)[1];
-  picture_params_.LoopFilter.LoopFilterLevelU = lf.filter_level_u;
-  picture_params_.LoopFilter.LoopFilterLevelV = lf.filter_level_v;
 
-  auto& cdef = picture_params_.CDEF;
-  cdef.CdefDampingMinus3 = 2;
-  cdef.CdefBits = 3;
-  for (uint32_t i = 0; i < (1 << cdef.CdefBits); i++) {
-    base::span(cdef.CdefYPriStrength)[i] = kCdefYPriStrength[i];
-    base::span(cdef.CdefUVPriStrength)[i] = kCdefUVPriStrength[i];
-    base::span(cdef.CdefYSecStrength)[i] = KCdefYSecStrength[i];
-    base::span(cdef.CdefUVSecStrength)[i] = kCdefUvSecStrength[i];
+  // Enable SCC tools will turn off CDEF, loop filter, etc on I-frame.
+  if (!picture_ctrl_.allow_intrabc) {
+    const aom::AV1LoopfilterLevel lf = software_brc_->GetLoopfilterLevel();
+    base::span(picture_params_.LoopFilter.LoopFilterLevel)[0] =
+        base::span(lf.filter_level)[0];
+    base::span(picture_params_.LoopFilter.LoopFilterLevel)[1] =
+        base::span(lf.filter_level)[1];
+    picture_params_.LoopFilter.LoopFilterLevelU = lf.filter_level_u;
+    picture_params_.LoopFilter.LoopFilterLevelV = lf.filter_level_v;
+
+    auto& cdef = picture_params_.CDEF;
+    cdef.CdefDampingMinus3 = 2;
+    cdef.CdefBits = 3;
+    for (uint32_t i = 0; i < (1 << cdef.CdefBits); i++) {
+      base::span(cdef.CdefYPriStrength)[i] = kCdefYPriStrength[i];
+      base::span(cdef.CdefUVPriStrength)[i] = kCdefUVPriStrength[i];
+      base::span(cdef.CdefYSecStrength)[i] = KCdefYSecStrength[i];
+      base::span(cdef.CdefUVSecStrength)[i] = kCdefUvSecStrength[i];
+    }
   }
 }
 
@@ -491,7 +548,7 @@
   input_arguments_.SequenceControlDesc.PictureTargetResolution = input_size_;
 
   // Fill picture_params_ for next encoded frame.
-  FillPictureControlParams(options.key_frame);
+  FillPictureControlParams(options);
 
   bool is_keyframe =
       picture_params_.FrameType == D3D12_VIDEO_ENCODER_AV1_FRAME_TYPE_KEY_FRAME;
@@ -554,7 +611,8 @@
   // Pack Frame OBU, see section 5.9 of the AV1 specification.
   pack_header.WriteOBUHeader(/*type=*/libgav1::kObuFrame, /*has_size=*/true);
   AV1BitstreamBuilder frame_obu = AV1BitstreamBuilder::BuildFrameHeaderOBU(
-      sequence_header_, FillAV1BuilderFrameHeader(picture_params_));
+      sequence_header_,
+      FillAV1BuilderFrameHeader(picture_ctrl_, picture_params_));
   CHECK_EQ(frame_obu.OutstandingBits() % 8, 0ull);
   pack_header.WriteValueInLeb128(frame_obu.OutstandingBits() / 8 +
                                  compressed_size);
diff --git a/media/gpu/windows/d3d12_video_encode_av1_delegate.h b/media/gpu/windows/d3d12_video_encode_av1_delegate.h
index 957dd06d..dfe574e 100644
--- a/media/gpu/windows/d3d12_video_encode_av1_delegate.h
+++ b/media/gpu/windows/d3d12_video_encode_av1_delegate.h
@@ -20,6 +20,11 @@
 class MEDIA_GPU_EXPORT D3D12VideoEncodeAV1Delegate
     : public D3D12VideoEncodeDelegate {
  public:
+  struct PictureControlFlags {
+    bool allow_screen_content_tools = false;
+    bool allow_intrabc = false;
+  };
+
   static std::vector<
       std::pair<VideoCodecProfile, std::vector<VideoPixelFormat>>>
   GetSupportedProfiles(ID3D12VideoDevice3* video_device);
@@ -46,7 +51,7 @@
   EncoderStatus::Or<size_t> ReadbackBitstream(
       base::span<uint8_t> bitstream_buffer) override;
 
-  void FillPictureControlParams(bool force_keyframe);
+  void FillPictureControlParams(const VideoEncoder::EncodeOptions& options);
 
   D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS input_arguments_{};
 
@@ -65,6 +70,15 @@
   // Bitrate allocation in bps.
   VideoBitrateAllocation bitrate_allocation_{Bitrate::Mode::kConstant};
 
+  // Enabled features for creating D3D12 AV1 encoder.
+  D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS enabled_features_{};
+
+  // Picture control flags for each Encoding frame.
+  PictureControlFlags picture_ctrl_{};
+
+  // The encoding content is a screen content.
+  bool is_screen_ = false;
+
   uint32_t framerate_ = 30;
   AV1BitstreamBuilder::SequenceHeader sequence_header_;
   D3D12VideoEncodeDecodedPictureBuffers<kAV1DPBMaxSize> dpb_;
diff --git a/media/mojo/services/video_decode_perf_history.h b/media/mojo/services/video_decode_perf_history.h
index e3de77f..4796ad0 100644
--- a/media/mojo/services/video_decode_perf_history.h
+++ b/media/mojo/services/video_decode_perf_history.h
@@ -6,8 +6,8 @@
 #define MEDIA_MOJO_SERVICES_VIDEO_DECODE_PERF_HISTORY_H_
 
 #include <stdint.h>
+
 #include <memory>
-#include <queue>
 
 #include "base/functional/callback.h"
 #include "base/metrics/field_trial_params.h"
diff --git a/media/mojo/services/webrtc_video_perf_history.h b/media/mojo/services/webrtc_video_perf_history.h
index 15adbdc8..89b44353 100644
--- a/media/mojo/services/webrtc_video_perf_history.h
+++ b/media/mojo/services/webrtc_video_perf_history.h
@@ -6,8 +6,8 @@
 #define MEDIA_MOJO_SERVICES_WEBRTC_VIDEO_PERF_HISTORY_H_
 
 #include <stdint.h>
+
 #include <memory>
-#include <queue>
 
 #include "base/functional/callback.h"
 #include "base/metrics/field_trial_params.h"
diff --git a/mojo/core/message_pipe_dispatcher.h b/mojo/core/message_pipe_dispatcher.h
index bdeaa3a..f4b1359 100644
--- a/mojo/core/message_pipe_dispatcher.h
+++ b/mojo/core/message_pipe_dispatcher.h
@@ -9,7 +9,6 @@
 
 #include <memory>
 #include <optional>
-#include <queue>
 
 #include "base/memory/raw_ptr_exclusion.h"
 #include "mojo/core/atomic_flag.h"
diff --git a/mojo/core/ports/node.h b/mojo/core/ports/node.h
index 18e5d97c..925beb31 100644
--- a/mojo/core/ports/node.h
+++ b/mojo/core/ports/node.h
@@ -8,7 +8,6 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <queue>
 #include <unordered_map>
 
 #include "base/component_export.h"
diff --git a/mojo/core/ports/port.h b/mojo/core/ports/port.h
index 4ed1aed1..b8fb752 100644
--- a/mojo/core/ports/port.h
+++ b/mojo/core/ports/port.h
@@ -7,7 +7,6 @@
 
 #include <map>
 #include <memory>
-#include <queue>
 #include <utility>
 #include <vector>
 
diff --git a/net/base/network_change_notifier_fuchsia_unittest.cc b/net/base/network_change_notifier_fuchsia_unittest.cc
index 335696d4..ba93d0d4 100644
--- a/net/base/network_change_notifier_fuchsia_unittest.cc
+++ b/net/base/network_change_notifier_fuchsia_unittest.cc
@@ -8,6 +8,7 @@
 #include <lib/fidl/cpp/binding.h>
 
 #include <memory>
+#include <queue>
 #include <string>
 #include <utility>
 #include <vector>
diff --git a/net/disk_cache/blockfile/backend_impl.cc b/net/disk_cache/blockfile/backend_impl.cc
index e9d3288..3053b6f 100644
--- a/net/disk_cache/blockfile/backend_impl.cc
+++ b/net/disk_cache/blockfile/backend_impl.cc
@@ -15,6 +15,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/containers/heap_array.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -1418,7 +1419,7 @@
       return false;
 
     data_->header.stats = address.value();
-    return stats_.Init(nullptr, 0, address);
+    return stats_.Init(base::span<uint8_t>(), address);
   }
 
   if (!address.is_block_file()) {
@@ -1431,14 +1432,16 @@
   if (!file)
     return false;
 
-  auto data = std::make_unique<char[]>(size);
+  auto data = base::HeapArray<uint8_t>::Uninit(size);
   size_t offset = address.start_block() * address.BlockSize() +
                   kBlockHeaderSize;
-  if (!file->Read(data.get(), size, offset))
+  if (!file->Read(data.data(), size, offset)) {
     return false;
+  }
 
-  if (!stats_.Init(data.get(), size, address))
+  if (!stats_.Init(data.as_span(), address)) {
     return false;
+  }
   if (GetCacheType() == net::DISK_CACHE && ShouldUpdateStats()) {
     stats_.InitSizeHistogram();
   }
@@ -1447,9 +1450,9 @@
 
 void BackendImpl::StoreStats() {
   int size = stats_.StorageSize();
-  auto data = std::make_unique<char[]>(size);
+  auto data = base::HeapArray<uint8_t>::Uninit(size);
   Addr address;
-  size = stats_.SerializeStats(data.get(), size, &address);
+  size = stats_.SerializeStats(data.as_span(), &address);
   DCHECK(size);
   if (!address.is_initialized())
     return;
@@ -1460,7 +1463,7 @@
 
   size_t offset = address.start_block() * address.BlockSize() +
                   kBlockHeaderSize;
-  file->Write(data.get(), size, offset);  // ignore result.
+  file->Write(data.data(), size, offset);  // ignore result.
 }
 
 void BackendImpl::RestartCache(bool failure) {
diff --git a/net/disk_cache/blockfile/stats.cc b/net/disk_cache/blockfile/stats.cc
index 67a55a5..fd612cb 100644
--- a/net/disk_cache/blockfile/stats.cc
+++ b/net/disk_cache/blockfile/stats.cc
@@ -2,18 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "net/disk_cache/blockfile/stats.h"
 
+#include <algorithm>
 #include <array>
 #include <bit>
 #include <cstdint>
 
 #include "base/check.h"
+#include "base/containers/span.h"
 #include "base/format_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -24,7 +21,7 @@
 
 struct OnDiskStats {
   int32_t signature;
-  int size;
+  uint32_t size;
   int data_sizes[disk_cache::Stats::kDataSizesLength];
   int64_t counters[disk_cache::Stats::MAX_COUNTER];
 };
@@ -54,12 +51,12 @@
 
   // We don't want to discard the whole cache every time we have one extra
   // counter; we keep old data if we can.
-  if (static_cast<unsigned int>(stats->size) > sizeof(*stats)) {
-    memset(stats, 0, sizeof(*stats));
+  auto stats_bytes = base::byte_span_from_ref(*stats);
+  if (stats->size > sizeof(*stats)) {
+    std::ranges::fill(stats_bytes, 0);
     stats->signature = kDiskSignature;
-  } else if (static_cast<unsigned int>(stats->size) != sizeof(*stats)) {
-    size_t delta = sizeof(*stats) - static_cast<unsigned int>(stats->size);
-    memset(reinterpret_cast<char*>(stats) + stats->size, 0, delta);
+  } else if (stats->size < sizeof(*stats)) {
+    std::ranges::fill(stats_bytes.subspan(stats->size), 0);
     stats->size = sizeof(*stats);
   }
 
@@ -70,35 +67,40 @@
 
 Stats::~Stats() = default;
 
-bool Stats::Init(void* data, int num_bytes, Addr address) {
+bool Stats::Init(base::span<uint8_t> data, Addr address) {
   OnDiskStats local_stats;
   OnDiskStats* stats = &local_stats;
-  if (!num_bytes) {
-    memset(stats, 0, sizeof(local_stats));
+  auto local_stats_bytes = base::byte_span_from_ref(local_stats);
+  if (data.empty()) {
+    std::ranges::fill(local_stats_bytes, 0);
     local_stats.signature = kDiskSignature;
     local_stats.size = sizeof(local_stats);
-  } else if (num_bytes >= static_cast<int>(sizeof(*stats))) {
-    stats = reinterpret_cast<OnDiskStats*>(data);
+  } else if (data.size() >= sizeof(*stats)) {
+    stats = reinterpret_cast<OnDiskStats*>(data.data());
     if (!VerifyStats(stats)) {
-      memset(&local_stats, 0, sizeof(local_stats));
-      if (memcmp(stats, &local_stats, sizeof(local_stats))) {
-        return false;
-      } else {
+      std::ranges::fill(local_stats_bytes, 0);
+      if (base::byte_span_from_ref(*stats).first(sizeof(local_stats)) ==
+          local_stats_bytes) {
         // The storage is empty which means that SerializeStats() was never
         // called on the last run. Just re-initialize everything.
         local_stats.signature = kDiskSignature;
         local_stats.size = sizeof(local_stats);
         stats = &local_stats;
+      } else {
+        return false;
       }
     }
   } else {
+    // Too few bytes.
     return false;
   }
 
   storage_addr_ = address;
 
-  memcpy(data_sizes_, stats->data_sizes, sizeof(data_sizes_));
-  memcpy(counters_, stats->counters, sizeof(counters_));
+  base::as_writable_byte_span(data_sizes_)
+      .copy_from_nonoverlapping(base::as_byte_span(stats->data_sizes));
+  base::as_writable_byte_span(counters_).copy_from_nonoverlapping(
+      base::as_byte_span(stats->counters));
 
   // Clean up old value.
   SetCounter(UNUSED, 0);
@@ -189,15 +191,19 @@
   return total;
 }
 
-int Stats::SerializeStats(void* data, int num_bytes, Addr* address) {
-  OnDiskStats* stats = reinterpret_cast<OnDiskStats*>(data);
-  if (num_bytes < static_cast<int>(sizeof(*stats)))
+int Stats::SerializeStats(base::span<uint8_t> data, Addr* address) {
+  if (data.size() < sizeof(OnDiskStats)) {
     return 0;
+  }
+  OnDiskStats* stats = reinterpret_cast<OnDiskStats*>(data.data());
 
   stats->signature = kDiskSignature;
   stats->size = sizeof(*stats);
-  memcpy(stats->data_sizes, data_sizes_, sizeof(data_sizes_));
-  memcpy(stats->counters, counters_, sizeof(counters_));
+
+  base::as_writable_byte_span(stats->data_sizes)
+      .copy_from_nonoverlapping(base::as_byte_span(data_sizes_));
+  base::as_writable_byte_span(stats->counters)
+      .copy_from_nonoverlapping(base::as_byte_span(counters_));
 
   *address = storage_addr_;
   return sizeof(*stats);
diff --git a/net/disk_cache/blockfile/stats.h b/net/disk_cache/blockfile/stats.h
index 961ec19..7904f77 100644
--- a/net/disk_cache/blockfile/stats.h
+++ b/net/disk_cache/blockfile/stats.h
@@ -8,6 +8,9 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <array>
+
+#include "base/containers/span.h"
 #include "base/strings/string_split.h"
 #include "net/base/net_export.h"
 #include "net/disk_cache/blockfile/addr.h"
@@ -55,7 +58,9 @@
   ~Stats();
 
   // Initializes this object with |data| from disk.
-  bool Init(void* data, int num_bytes, Addr address);
+  // The data may be modified in case of some recoverable errors or format
+  // changes.
+  bool Init(base::span<uint8_t> data, Addr address);
 
   // Generates a size distribution histogram.
   void InitSizeHistogram();
@@ -79,7 +84,7 @@
 
   // Writes the stats into |data|, to be stored at the given cache address.
   // Returns the number of bytes copied.
-  int SerializeStats(void* data, int num_bytes, Addr* address);
+  int SerializeStats(base::span<uint8_t> data, Addr* address);
 
  private:
   // Supports generation of SizeStats histogram data.
@@ -88,8 +93,8 @@
   int GetRatio(Counters hit, Counters miss) const;
 
   Addr storage_addr_;
-  int data_sizes_[kDataSizesLength];
-  int64_t counters_[MAX_COUNTER];
+  std::array<int, kDataSizesLength> data_sizes_;
+  std::array<int64_t, MAX_COUNTER> counters_;
 };
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/blockfile/stats_unittest.cc b/net/disk_cache/blockfile/stats_unittest.cc
index 8fccdd9..1a3350395 100644
--- a/net/disk_cache/blockfile/stats_unittest.cc
+++ b/net/disk_cache/blockfile/stats_unittest.cc
@@ -2,54 +2,53 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
-#pragma allow_unsafe_libc_calls
-#endif
-
 #include "net/disk_cache/blockfile/stats.h"
 
+#include <algorithm>
+#include <cstdint>
 #include <memory>
 
+#include "base/containers/heap_array.h"
+#include "base/containers/span.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(DiskCacheStatsTest, Init) {
   disk_cache::Stats stats;
-  EXPECT_TRUE(stats.Init(nullptr, 0, disk_cache::Addr()));
+  EXPECT_TRUE(stats.Init(base::span<uint8_t>(), disk_cache::Addr()));
   EXPECT_EQ(0, stats.GetCounter(disk_cache::Stats::TRIM_ENTRY));
 }
 
 TEST(DiskCacheStatsTest, InitWithEmptyBuffer) {
   disk_cache::Stats stats;
   int required_len = stats.StorageSize();
-  auto storage = std::make_unique<char[]>(required_len);
-  memset(storage.get(), 0, required_len);
+  auto storage = base::HeapArray<uint8_t>::Uninit(required_len);
+  std::ranges::fill(storage.as_span(), 0);
 
-  ASSERT_TRUE(stats.Init(storage.get(), required_len, disk_cache::Addr()));
+  ASSERT_TRUE(stats.Init(storage.as_span(), disk_cache::Addr()));
   EXPECT_EQ(0, stats.GetCounter(disk_cache::Stats::TRIM_ENTRY));
 }
 
 TEST(DiskCacheStatsTest, FailsInit) {
   disk_cache::Stats stats;
   int required_len = stats.StorageSize();
-  auto storage = std::make_unique<char[]>(required_len);
-  memset(storage.get(), 0, required_len);
+  auto storage = base::HeapArray<uint8_t>::Uninit(required_len);
+  std::ranges::fill(storage.as_span(), 0);
 
   // Try a small buffer.
   EXPECT_LT(200, required_len);
   disk_cache::Addr addr;
-  EXPECT_FALSE(stats.Init(storage.get(), 200, addr));
+  EXPECT_FALSE(stats.Init(storage.as_span().first(200u), addr));
 
   // Try a buffer with garbage.
-  memset(storage.get(), 'a', required_len);
-  EXPECT_FALSE(stats.Init(storage.get(), required_len, addr));
+  std::ranges::fill(storage.as_span(), 'a');
+  EXPECT_FALSE(stats.Init(storage.as_span(), addr));
 }
 
 TEST(DiskCacheStatsTest, SaveRestore) {
   auto stats = std::make_unique<disk_cache::Stats>();
 
   disk_cache::Addr addr(5);
-  ASSERT_TRUE(stats->Init(nullptr, 0, addr));
+  ASSERT_TRUE(stats->Init(base::span<uint8_t>(), addr));
   stats->SetCounter(disk_cache::Stats::CREATE_ERROR, 11);
   stats->SetCounter(disk_cache::Stats::DOOM_ENTRY, 13);
   stats->OnEvent(disk_cache::Stats::MIN_COUNTER);
@@ -57,14 +56,15 @@
   stats->OnEvent(disk_cache::Stats::DOOM_RECENT);
 
   int required_len = stats->StorageSize();
-  auto storage = std::make_unique<char[]>(required_len);
+  auto storage = base::HeapArray<uint8_t>::Uninit(required_len);
   disk_cache::Addr out_addr;
-  int real_len = stats->SerializeStats(storage.get(), required_len, &out_addr);
+  int real_len = stats->SerializeStats(storage.as_span(), &out_addr);
   EXPECT_GE(required_len, real_len);
   EXPECT_EQ(out_addr, addr);
 
   stats = std::make_unique<disk_cache::Stats>();
-  ASSERT_TRUE(stats->Init(storage.get(), real_len, addr));
+  ASSERT_TRUE(stats->Init(
+      storage.as_span().first(static_cast<size_t>(real_len)), addr));
   EXPECT_EQ(1, stats->GetCounter(disk_cache::Stats::MIN_COUNTER));
   EXPECT_EQ(1, stats->GetCounter(disk_cache::Stats::TRIM_ENTRY));
   EXPECT_EQ(1, stats->GetCounter(disk_cache::Stats::DOOM_RECENT));
@@ -77,7 +77,7 @@
   // Now pass the whole buffer. It shoulod not matter that there is unused
   // space at the end.
   stats = std::make_unique<disk_cache::Stats>();
-  ASSERT_TRUE(stats->Init(storage.get(), required_len, addr));
+  ASSERT_TRUE(stats->Init(storage.as_span(), addr));
   EXPECT_EQ(1, stats->GetCounter(disk_cache::Stats::MIN_COUNTER));
   EXPECT_EQ(1, stats->GetCounter(disk_cache::Stats::TRIM_ENTRY));
   EXPECT_EQ(1, stats->GetCounter(disk_cache::Stats::DOOM_RECENT));
diff --git a/net/disk_cache/blockfile/storage_block_unittest.cc b/net/disk_cache/blockfile/storage_block_unittest.cc
index ddd0cfd..b3257f6 100644
--- a/net/disk_cache/blockfile/storage_block_unittest.cc
+++ b/net/disk_cache/blockfile/storage_block_unittest.cc
@@ -2,15 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
-#pragma allow_unsafe_libc_calls
-#endif
+#include "net/disk_cache/blockfile/storage_block.h"
 
+#include <algorithm>
+
+#include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "net/disk_cache/blockfile/disk_format.h"
 #include "net/disk_cache/blockfile/storage_block-inl.h"
-#include "net/disk_cache/blockfile/storage_block.h"
 #include "net/disk_cache/disk_cache_test_base.h"
 #include "net/disk_cache/disk_cache_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -24,7 +23,7 @@
   ASSERT_TRUE(file->Init(filename, 8192));
 
   CacheEntryBlock entry1(file.get(), disk_cache::Addr(0xa0010001));
-  memset(entry1.Data(), 0, sizeof(disk_cache::EntryStore));
+  std::ranges::fill(base::byte_span_from_ref(*entry1.Data()), 0);
   entry1.Data()->hash = 0xaa5555aa;
   entry1.Data()->rankings_node = 0xa0010002;
 
diff --git a/net/disk_cache/simple/simple_entry_format.cc b/net/disk_cache/simple/simple_entry_format.cc
index de2616c..1034b20f 100644
--- a/net/disk_cache/simple/simple_entry_format.cc
+++ b/net/disk_cache/simple/simple_entry_format.cc
@@ -2,30 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
-#pragma allow_unsafe_libc_calls
-#endif
-
 #include "net/disk_cache/simple/simple_entry_format.h"
 
-#include <cstring>
+#include "base/containers/span.h"
 
 namespace disk_cache {
 
 SimpleFileHeader::SimpleFileHeader() {
-  // Make hashing repeatable: leave no padding bytes untouched.
-  std::memset(this, 0, sizeof(*this));
+  // We don't want unset holes in types stored to disk.
+  static_assert(std::has_unique_object_representations_v<SimpleFileHeader>,
+                "SimpleFileHeader should have no implicit padding bytes");
 }
 
 SimpleFileEOF::SimpleFileEOF() {
-  // Make hashing repeatable: leave no padding bytes untouched.
-  std::memset(this, 0, sizeof(*this));
+  // We don't want unset holes in types stored to disk.
+  static_assert(std::has_unique_object_representations_v<SimpleFileEOF>,
+                "SimpleFileEOF should have no implicit padding bytes");
 }
 
 SimpleFileSparseRangeHeader::SimpleFileSparseRangeHeader() {
-  // Make hashing repeatable: leave no padding bytes untouched.
-  std::memset(this, 0, sizeof(*this));
+  // We don't want unset holes in types stored to disk.
+  static_assert(
+      std::has_unique_object_representations_v<SimpleFileSparseRangeHeader>,
+      "SimpleFileSparseRangeHeader should have no implicit padding bytes");
 }
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_entry_format.h b/net/disk_cache/simple/simple_entry_format.h
index 6d2304f..4d4472d 100644
--- a/net/disk_cache/simple/simple_entry_format.h
+++ b/net/disk_cache/simple/simple_entry_format.h
@@ -53,10 +53,10 @@
 struct NET_EXPORT_PRIVATE SimpleFileHeader {
   SimpleFileHeader();
 
-  uint64_t initial_magic_number;
-  uint32_t version;
-  uint32_t key_length;
-  uint32_t key_hash;
+  uint64_t initial_magic_number = 0;
+  uint32_t version = 0;
+  uint32_t key_length = 0;
+  uint32_t key_hash = 0;
 
   // Avoid implicit padding so `std::has_unique_object_representations_v<>` will
   // hold.
@@ -72,11 +72,11 @@
 
   SimpleFileEOF();
 
-  uint64_t final_magic_number;
-  uint32_t flags;
-  uint32_t data_crc32;
+  uint64_t final_magic_number = 0;
+  uint32_t flags = 0;
+  uint32_t data_crc32 = 0;
   // |stream_size| is only used in the EOF record for stream 0.
-  uint32_t stream_size;
+  uint32_t stream_size = 0;
 
   // Avoid implicit padding so `std::has_unique_object_representations_v<>` will
   // hold.
@@ -86,10 +86,10 @@
 struct SimpleFileSparseRangeHeader {
   SimpleFileSparseRangeHeader();
 
-  uint64_t sparse_range_magic_number;
-  int64_t offset;
-  int64_t length;
-  uint32_t data_crc32;
+  uint64_t sparse_range_magic_number = 0;
+  int64_t offset = 0;
+  int64_t length = 0;
+  uint32_t data_crc32 = 0;
 
   // Avoid implicit padding so `std::has_unique_object_representations_v<>` will
   // hold.
diff --git a/net/disk_cache/simple/simple_version_upgrade.cc b/net/disk_cache/simple/simple_version_upgrade.cc
index 01622f77..6359f91 100644
--- a/net/disk_cache/simple/simple_version_upgrade.cc
+++ b/net/disk_cache/simple/simple_version_upgrade.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
-#pragma allow_unsafe_libc_calls
-#endif
-
 #include "net/disk_cache/simple/simple_version_upgrade.h"
 
 #include <cstring>
@@ -64,8 +59,9 @@
 namespace disk_cache {
 
 FakeIndexData::FakeIndexData() {
-  // Make hashing repeatable: leave no padding bytes untouched.
-  std::memset(this, 0, sizeof(*this));
+  // We don't want unset holes in types stored to disk.
+  static_assert(std::has_unique_object_representations_v<FakeIndexData>,
+                "FakeIndexData should have no implicit padding bytes");
 }
 
 // Some points about the Upgrade process are still not clear:
diff --git a/net/disk_cache/simple/simple_version_upgrade.h b/net/disk_cache/simple/simple_version_upgrade.h
index 4ce0b7b..cebb18d 100644
--- a/net/disk_cache/simple/simple_version_upgrade.h
+++ b/net/disk_cache/simple/simple_version_upgrade.h
@@ -61,15 +61,15 @@
   FakeIndexData();
 
   // Must be equal to kSimpleInitialMagicNumber.
-  uint64_t initial_magic_number;
+  uint64_t initial_magic_number = 0;
 
   // Must be equal kSimpleVersion when the cache backend is instantiated.
-  uint32_t version;
+  uint32_t version = 0;
 
   // These must be zero. The first was used for experiment type (With a max
   // valid value of 2), and the second was used for an experiment parameter.
-  uint32_t zero;
-  uint32_t zero2;
+  uint32_t zero = 0;
+  uint32_t zero2 = 0;
 
   // Avoid implicit padding so `std::has_unique_object_representations_v<>` will
   // hold.
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index af996d2..d1711c3 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -381,6 +381,10 @@
   spdy_session_pool_.CloseCurrentIdleSessions(net_log_reason_utf8);
 }
 
+void HttpNetworkSession::SetTLS13EarlyDataEnabled(bool enabled) {
+  params_.enable_early_data = enabled;
+}
+
 bool HttpNetworkSession::IsQuicEnabled() const {
   return params_.enable_quic;
 }
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index a68f6817..43aa7bc 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -295,6 +295,8 @@
     return application_settings_;
   }
 
+  void SetTLS13EarlyDataEnabled(bool enabled);
+
   // Evaluates if QUIC is enabled for new streams.
   bool IsQuicEnabled() const;
 
diff --git a/net/http/http_stream_pool_attempt_manager.cc b/net/http/http_stream_pool_attempt_manager.cc
index 69d9a64..41f05d764 100644
--- a/net/http/http_stream_pool_attempt_manager.cc
+++ b/net/http/http_stream_pool_attempt_manager.cc
@@ -190,8 +190,6 @@
     ssl_config.privacy_mode = stream_key().privacy_mode();
     ssl_config.disable_cert_verification_network_fetches =
         stream_key().disable_cert_network_fetches();
-    ssl_config.early_data_enabled =
-        http_network_session()->params().enable_early_data;
 
     ssl_config.alpn_protos = http_network_session()->GetAlpnProtos();
     ssl_config.application_settings =
@@ -445,8 +443,12 @@
   CHECK(service_endpoint_request_);
   CHECK(service_endpoint_request_->EndpointsCryptoReady());
 
+  SSLConfig ssl_config = *base_ssl_config_;
+  ssl_config.early_data_enabled =
+      http_network_session()->params().enable_early_data;
+
   if (!IsEchEnabled()) {
-    return *base_ssl_config_;
+    return ssl_config;
   }
 
   const bool svcb_optional = IsSvcbOptional();
@@ -458,7 +460,6 @@
                                                       ? endpoint.ipv4_endpoints
                                                       : endpoint.ipv6_endpoints;
     if (base::Contains(ip_endpoints, ip_endpoint)) {
-      SSLConfig ssl_config = *base_ssl_config_;
       ssl_config.ech_config_list = endpoint.metadata.ech_config_list;
       return ssl_config;
     }
diff --git a/net/socket/ssl_server_socket_impl.cc b/net/socket/ssl_server_socket_impl.cc
index b67a8de1..f5aa5db 100644
--- a/net/socket/ssl_server_socket_impl.cc
+++ b/net/socket/ssl_server_socket_impl.cc
@@ -586,6 +586,7 @@
                                 &ssl_info->connection_status);
 
   ssl_info->early_data_received = early_data_received_;
+  ssl_info->early_data_accepted = SSL_early_data_accepted(ssl_.get());
   ssl_info->encrypted_client_hello = SSL_ech_accepted(ssl_.get());
   ssl_info->handshake_type = SSL_session_reused(ssl_.get())
                                  ? SSLInfo::HANDSHAKE_RESUME
diff --git a/net/ssl/ssl_info.h b/net/ssl/ssl_info.h
index c3e3becc..c72d6526 100644
--- a/net/ssl/ssl_info.h
+++ b/net/ssl/ssl_info.h
@@ -83,6 +83,10 @@
   // set for server sockets.
   bool early_data_received = false;
 
+  // True if early data was accepted on the server. This field is only
+  // set for server sockets.
+  bool early_data_accepted = false;
+
   // True if the connection negotiated the Encrypted ClientHello extension.
   bool encrypted_client_hello = false;
 
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index d7ffce2..55efc2c 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -1173,29 +1173,29 @@
 
 #if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
 void URLRequestHttpJob::ProcessDeviceBoundSessionsHeader() {
-  if (!request_->allows_device_bound_session_registration() &&
-      !features::kDeviceBoundSessionsForceEnableForTesting.Get()) {
-    return;
-  }
-
   device_bound_sessions::SessionService* service =
       request_->context()->device_bound_session_service();
   if (!service) {
     return;
   }
 
+  const auto& request_url = request_->url();
+  auto* headers = GetResponseHeaders();
+
   // If response header Sec-Session-Registration is present and configured
   // appropriately, trigger a registration request per header value to attempt
   // to create a new session.
-  const auto& request_url = request_->url();
-  auto* headers = GetResponseHeaders();
-  std::vector<device_bound_sessions::RegistrationFetcherParam> params =
-      device_bound_sessions::RegistrationFetcherParam::CreateIfValid(
-          request_url, headers);
-  for (auto& param : params) {
-    service->RegisterBoundSession(
-        request_->device_bound_session_access_callback(), std::move(param),
-        request_->isolation_info(), request_->net_log(), request_->initiator());
+  if (request_->allows_device_bound_session_registration() ||
+      features::kDeviceBoundSessionsForceEnableForTesting.Get()) {
+    std::vector<device_bound_sessions::RegistrationFetcherParam> params =
+        device_bound_sessions::RegistrationFetcherParam::CreateIfValid(
+            request_url, headers);
+    for (auto& param : params) {
+      service->RegisterBoundSession(
+          request_->device_bound_session_access_callback(), std::move(param),
+          request_->isolation_info(), request_->net_log(),
+          request_->initiator());
+    }
   }
 
   // If response header Sec-Session-Challenge is present and configured
diff --git a/pdf/BUILD.gn b/pdf/BUILD.gn
index cdd15b24..609d018 100644
--- a/pdf/BUILD.gn
+++ b/pdf/BUILD.gn
@@ -262,6 +262,7 @@
       ]
 
       public_deps += [ "//services/screen_ai/public/mojom" ]
+      deps += [ "//services/screen_ai/public/cpp:utilities" ]
 
       if (is_chromeos) {
         sources += [
diff --git a/pdf/DEPS b/pdf/DEPS
index 580b2eb..b010b77 100644
--- a/pdf/DEPS
+++ b/pdf/DEPS
@@ -7,6 +7,7 @@
   "+printing",
   "+services/network/public/mojom/referrer_policy.mojom-shared.h",
   "+services/screen_ai/buildflags",
+  "+services/screen_ai/public/cpp/utilities.h",
   "+services/screen_ai/public/mojom/screen_ai_service.mojom.h",
   "+services/screen_ai/public/mojom/screen_ai_service.mojom-forward.h",
   "+third_party/blink/public",
diff --git a/pdf/pdfium/pdfium_ocr.cc b/pdf/pdfium/pdfium_ocr.cc
index 3e67f8ee..34a3ca3 100644
--- a/pdf/pdfium/pdfium_ocr.cc
+++ b/pdf/pdfium/pdfium_ocr.cc
@@ -39,16 +39,22 @@
 SkBitmap GetImageForOcr(FPDF_DOCUMENT doc,
                         FPDF_PAGE page,
                         FPDF_PAGEOBJECT page_object,
-                        uint32_t max_image_dimension) {
+                        uint32_t max_image_dimension,
+                        bool rotate_image_to_upright) {
   SkBitmap bitmap;
 
   if (FPDFPageObj_GetType(page_object) != FPDF_PAGEOBJ_IMAGE) {
     return bitmap;
   }
 
-  // OCR needs the image with at most `kMaxNeededDPI` dpi resolution. To get it,
-  // the image transform matrix is set to an appropriate scale, the bitmap is
-  // extracted, and then the original matrix is restored.
+  // If image is resized so that it is not shown, no need to OCR it.
+  if (GetImageSize(page_object).IsEmpty()) {
+    return bitmap;
+  }
+
+  // OCR needs the image with at most `max_image_dimension` resolution. To get
+  // it, the image transform matrix is set to an appropriate scale, the bitmap
+  // is extracted, and then the original matrix is restored.
   FS_MATRIX original_matrix;
   if (!FPDFPageObj_GetMatrix(page_object, &original_matrix)) {
     DLOG(ERROR) << "Failed to get original matrix";
@@ -80,25 +86,37 @@
     effective_height = pixel_height;
   }
 
-  // Scale the matrix to get image with highest resolution and keep the
-  // rotation. If image is stretched differently in horizontal and vertical
-  // directions, the one with no enlargement of the original height and width is
-  // selected.
-  float width_scale = hypotf(original_matrix.a, original_matrix.c);
-  float height_scale = hypotf(original_matrix.b, original_matrix.d);
-  if (width_scale == 0 || height_scale == 0) {
-    return bitmap;
-  }
-  float ratio =
-      std::min(effective_width / width_scale, effective_height / height_scale);
-  const FS_MATRIX new_matrix = {
-      original_matrix.a * ratio, original_matrix.b * ratio,
-      original_matrix.c * ratio, original_matrix.d * ratio,
-      original_matrix.e,         original_matrix.f};
+  if (rotate_image_to_upright) {
+    // Scale the matrix to get image with highest resolution and keep the
+    // rotation. If image is stretched differently in horizontal and vertical
+    // directions, the one with no enlargement of the original height and width
+    // is selected.
+    float width_scale = hypotf(original_matrix.a, original_matrix.c);
+    float height_scale = hypotf(original_matrix.b, original_matrix.d);
+    if (width_scale == 0 || height_scale == 0) {
+      return bitmap;
+    }
+    float ratio = std::min(effective_width / width_scale,
+                           effective_height / height_scale);
+    const FS_MATRIX new_matrix = {
+        original_matrix.a * ratio, original_matrix.b * ratio,
+        original_matrix.c * ratio, original_matrix.d * ratio,
+        original_matrix.e,         original_matrix.f};
 
-  if (!FPDFPageObj_SetMatrix(page_object, &new_matrix)) {
-    DLOG(ERROR) << "Failed to set new matrix on image";
-    return bitmap;
+    if (!FPDFPageObj_SetMatrix(page_object, &new_matrix)) {
+      DLOG(ERROR) << "Failed to set new matrix on image";
+      return bitmap;
+    }
+  } else {
+    // Scale the image to the highest (capped) resolution, but do not rotate the
+    // image to make it upright.
+    const FS_MATRIX new_matrix = {effective_width,  0, 0,
+                                  effective_height, 0, 0};
+
+    if (!FPDFPageObj_SetMatrix(page_object, &new_matrix)) {
+      DLOG(ERROR) << "Failed to set new matrix on image";
+      return bitmap;
+    }
   }
 
   ScopedFPDFBitmap raw_bitmap(
diff --git a/pdf/pdfium/pdfium_ocr.h b/pdf/pdfium/pdfium_ocr.h
index 04e2123..17c2536 100644
--- a/pdf/pdfium/pdfium_ocr.h
+++ b/pdf/pdfium/pdfium_ocr.h
@@ -11,10 +11,14 @@
 
 namespace chrome_pdf {
 
+// TODO(crbug.com/c/360803943): Remove `rotate_image_to_upright` when PDF OCR
+// support is removed and set the default behavior to
+// rotate_image_to_upright = false.
 SkBitmap GetImageForOcr(FPDF_DOCUMENT doc,
                         FPDF_PAGE page,
                         FPDF_PAGEOBJECT page_object,
-                        uint32_t max_image_dimension);
+                        uint32_t max_image_dimension,
+                        bool rotate_image_to_upright);
 
 // Returns image bound's size in page coordinates. Returns (0,0) if fails.
 gfx::SizeF GetImageSize(FPDF_PAGEOBJECT page_object);
diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc
index c130c5f..01ddaf5 100644
--- a/pdf/pdfium/pdfium_page.cc
+++ b/pdf/pdfium/pdfium_page.cc
@@ -951,9 +951,15 @@
                                     int max_image_dimension) {
   FPDF_PAGE page = GetPage();
   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, page_object_index);
+  bool rotate_image_to_upright =
+      !base::FeatureList::IsEnabled(chrome_pdf::features::kPdfSearchify);
   SkBitmap bitmap = ::chrome_pdf::GetImageForOcr(
-      engine_->doc(), page, page_object, max_image_dimension);
+      engine_->doc(), page, page_object, max_image_dimension,
+      rotate_image_to_upright);
 
+  if (!rotate_image_to_upright) {
+    return bitmap;
+  }
   SkBitmapOperations::RotationAmount rotation;
   switch (FPDFPage_GetRotation(page)) {
     case 0:
diff --git a/pdf/pdfium/pdfium_page_unittest.cc b/pdf/pdfium/pdfium_page_unittest.cc
index e805113..bdc1187 100644
--- a/pdf/pdfium/pdfium_page_unittest.cc
+++ b/pdf/pdfium/pdfium_page_unittest.cc
@@ -17,6 +17,7 @@
 #include "build/build_config.h"
 #include "pdf/accessibility_structs.h"
 #include "pdf/buildflags.h"
+#include "pdf/pdf_features.h"
 #include "pdf/pdfium/pdfium_engine.h"
 #include "pdf/pdfium/pdfium_test_base.h"
 #include "pdf/test/test_client.h"
@@ -624,12 +625,20 @@
   page.PopulateTextRunTypeAndImageAltText(text_runs);
   ASSERT_EQ(1u, page.images_.size());
 
-  // This page is rotated, therefore the extracted image size is 25x100 while
-  // the stored image is 100x25.
   SkBitmap image_bitmap = engine->GetImageForOcr(
       /*page_index=*/0, page.images_[0].page_object_index);
-  EXPECT_EQ(image_bitmap.width(), 25);
-  EXPECT_EQ(image_bitmap.height(), 100);
+
+  if (base::FeatureList::IsEnabled(chrome_pdf::features::kPdfSearchify)) {
+    // When PDF Searchify is enabled, page rotation does not affect the images
+    // that are sent to OCR.
+    EXPECT_EQ(image_bitmap.width(), 100);
+    EXPECT_EQ(image_bitmap.height(), 25);
+  } else {
+    // This page is rotated, therefore the extracted image size is 25x100 while
+    // the stored image is 100x25.
+    EXPECT_EQ(image_bitmap.width(), 25);
+    EXPECT_EQ(image_bitmap.height(), 100);
+  }
 }
 
 TEST_P(PDFiumPageImageForOcrTest, NonImage) {
diff --git a/pdf/pdfium/pdfium_searchify.cc b/pdf/pdfium/pdfium_searchify.cc
index e4f0ecd..8e8119c0 100644
--- a/pdf/pdfium/pdfium_searchify.cc
+++ b/pdf/pdfium/pdfium_searchify.cc
@@ -25,6 +25,7 @@
 #include "pdf/pdfium/pdfium_mem_buffer_file_write.h"
 #include "pdf/pdfium/pdfium_ocr.h"
 #include "pdf/pdfium/pdfium_searchify_font.h"
+#include "services/screen_ai/public/cpp/utilities.h"
 #include "services/screen_ai/public/mojom/screen_ai_service.mojom.h"
 #include "third_party/pdfium/public/cpp/fpdf_scopers.h"
 #include "third_party/pdfium/public/fpdf_edit.h"
@@ -42,10 +43,6 @@
 
 namespace {
 
-// The maximum image dimension which is processed without downsampling by OCR.
-// TODO(crbug.com/413318481): Get this from OCR service.
-constexpr int kMaxImageDimensionForOcr = 2048;
-
 std::vector<uint32_t> Utf8ToCharcodes(const std::string& string) {
   std::u16string utf16_str = base::UTF8ToUTF16(string);
   std::vector<uint32_t> charcodes;
@@ -289,7 +286,8 @@
       // GetImageForOcr() checks for null `image`.
       FPDF_PAGEOBJECT image = FPDFPage_GetObject(page.get(), object_index);
       SkBitmap bitmap = GetImageForOcr(document.get(), page.get(), image,
-                                       kMaxImageDimensionForOcr);
+                                       screen_ai::GetMaxDimensionForOCR(),
+                                       /*rotate_image_to_upright=*/false);
       // The object is not an image or failed to get the bitmap from the image.
       if (bitmap.empty()) {
         continue;
diff --git a/remoting/protocol/spake2_authenticator.h b/remoting/protocol/spake2_authenticator.h
index 3ce42d0..1163618 100644
--- a/remoting/protocol/spake2_authenticator.h
+++ b/remoting/protocol/spake2_authenticator.h
@@ -6,7 +6,6 @@
 #define REMOTING_PROTOCOL_SPAKE2_AUTHENTICATOR_H_
 
 #include <memory>
-#include <queue>
 #include <string>
 
 #include "base/compiler_specific.h"
diff --git a/remoting/signaling/fake_signal_strategy.h b/remoting/signaling/fake_signal_strategy.h
index 35b91cd..ede3c64f 100644
--- a/remoting/signaling/fake_signal_strategy.h
+++ b/remoting/signaling/fake_signal_strategy.h
@@ -5,7 +5,6 @@
 #ifndef REMOTING_SIGNALING_FAKE_SIGNAL_STRATEGY_H_
 #define REMOTING_SIGNALING_FAKE_SIGNAL_STRATEGY_H_
 
-#include <queue>
 #include <string>
 
 #include "base/memory/weak_ptr.h"
diff --git a/sandbox/policy/linux/landlock_gpu_policy_android.cc b/sandbox/policy/linux/landlock_gpu_policy_android.cc
index 75d921c8..75840b4b 100644
--- a/sandbox/policy/linux/landlock_gpu_policy_android.cc
+++ b/sandbox/policy/linux/landlock_gpu_policy_android.cc
@@ -71,9 +71,12 @@
   }
 
   std::vector<std::string> allowed_ro_paths = {
-      // RO access to /data because there may be sub-directories that don't
+      // RO access to /data/app because there may be sub-directories that don't
       // exist yet at policy creation.
-      "/data", "/proc", "/sys", "/var"};
+      "/data/app",
+      // Allow read-only access to /proc/self. This is needed for the process
+      // to introspect its own state.
+      "/proc/self", "/sys", "/var"};
   uint64_t ro_access =
       LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR;
   if (!AddRulesToPolicy(ruleset_fd.get(), allowed_ro_paths, ro_access)) {
diff --git a/services/device/usb/mojo/device_manager_impl.h b/services/device/usb/mojo/device_manager_impl.h
index 0d41fd0..b10bdf3 100644
--- a/services/device/usb/mojo/device_manager_impl.h
+++ b/services/device/usb/mojo/device_manager_impl.h
@@ -6,7 +6,6 @@
 #define SERVICES_DEVICE_USB_MOJO_DEVICE_MANAGER_IMPL_H_
 
 #include <memory>
-#include <queue>
 #include <set>
 #include <string>
 #include <vector>
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 65c057d..aa4fce1 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -1219,6 +1219,12 @@
   proxy_lookup_requests_.erase(it);
 }
 
+void NetworkContext::SetTLS13EarlyDataEnabled(bool enabled) {
+  url_request_context_->http_transaction_factory()
+      ->GetSession()
+      ->SetTLS13EarlyDataEnabled(enabled);
+}
+
 void NetworkContext::DisableQuic() {
   url_request_context_->http_transaction_factory()->GetSession()->DisableQuic();
 }
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 2f8fe079..1378785 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -580,6 +580,8 @@
       mojo::PendingReceiver<network::mojom::DeviceBoundSessionManager>
           device_bound_session_manager) override;
 
+  void SetTLS13EarlyDataEnabled(bool enabled);
+
   // Destroys |request| when a proxy lookup completes.
   void OnProxyLookupComplete(ProxyLookupRequest* proxy_lookup_request);
 
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index ccb1003f..6466e66 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -1070,6 +1070,12 @@
           {base::TaskPriority::USER_BLOCKING}));
 }
 
+void NetworkService::SetTLS13EarlyDataEnabled(bool enabled) {
+  for (NetworkContext* network_context : network_contexts_) {
+    network_context->SetTLS13EarlyDataEnabled(enabled);
+  }
+}
+
 void NetworkService::StartNetLogBounded(base::File file,
                                         uint64_t max_total_size,
                                         net::NetLogCaptureMode capture_mode,
diff --git a/services/network/network_service.h b/services/network/network_service.h
index e9b9d95..ad40223 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -271,6 +271,8 @@
       mojo::PendingRemote<network::mojom::URLLoaderClient>
           dest_url_loader_client) override;
 
+  void SetTLS13EarlyDataEnabled(bool enabled) override;
+
   void StartNetLogBounded(base::File file,
                           uint64_t max_total_size,
                           net::NetLogCaptureMode capture_mode,
diff --git a/services/network/partial_decoder.h b/services/network/partial_decoder.h
index 4cf18430..47563fb3 100644
--- a/services/network/partial_decoder.h
+++ b/services/network/partial_decoder.h
@@ -5,7 +5,6 @@
 #ifndef SERVICES_NETWORK_PARTIAL_DECODER_H_
 #define SERVICES_NETWORK_PARTIAL_DECODER_H_
 
-#include <queue>
 #include <string>
 #include <vector>
 
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index c578dac..d82a52c1 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -476,4 +476,7 @@
       pending_receiver<URLLoaderClient> source_url_loader_client,
       pending_receiver<URLLoader> dest_url_loader,
       pending_remote<URLLoaderClient> dest_url_loader_client);
+
+  // Enables or disables TLS 1.3 Early Data.
+  SetTLS13EarlyDataEnabled(bool enabled);
 };
diff --git a/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc b/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc
index 90f905f..57140dd 100644
--- a/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc
+++ b/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc
@@ -561,8 +561,12 @@
 
     global_dump->process_dumps.push_back(std::move(pmd));
   }
-  global_dump->aggregated_metrics =
-      ComputeGlobalNativeCodeResidentMemoryKb(pid_to_os_dump);
+  if (!request->args.memory_footprint_only) {
+    global_dump->aggregated_metrics =
+        ComputeGlobalNativeCodeResidentMemoryKb(pid_to_os_dump);
+  } else {
+    global_dump->aggregated_metrics = mojom::AggregatedMetrics::New();
+  }
 
   const bool global_success = request->failed_memory_dump_count == 0;
 
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
index 0691956..03d5ed9 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
@@ -351,27 +351,30 @@
 
 #if BUILDFLAG(IS_ANDROID)
 #if BUILDFLAG(SUPPORTS_CODE_ORDERING)
-  if (!base::android::AreAnchorsSane()) {
-    DLOG(WARNING) << "Incorrect code ordering";
-    return false;
+  if (flags.Has(mojom::MemDumpFlags::MEM_DUMP_PAGES_BITMAP)) {
+    if (!base::android::AreAnchorsSane()) {
+      DLOG(WARNING) << "Incorrect code ordering";
+      return false;
+    }
+
+    std::vector<uint8_t> accessed_pages_bitmap;
+    OSMetrics::MappedAndResidentPagesDumpState state =
+        OSMetrics::GetMappedAndResidentPages(base::android::kStartOfText,
+                                             base::android::kEndOfText,
+                                             &accessed_pages_bitmap);
+    UMA_HISTOGRAM_ENUMERATION(
+        "Memory.NativeLibrary.MappedAndResidentMemoryFootprintCollectionStatus",
+        state);
+
+    // MappedAndResidentPagesDumpState |state| can be |kAccessPagemapDenied|
+    // for Android devices running a kernel version < 4.4 or because the process
+    // is not "dumpable", as described in proc(5).
+    if (state != OSMetrics::MappedAndResidentPagesDumpState::kSuccess) {
+      return state != OSMetrics::MappedAndResidentPagesDumpState::kFailure;
+    }
+
+    dump->native_library_pages_bitmap = std::move(accessed_pages_bitmap);
   }
-
-  std::vector<uint8_t> accessed_pages_bitmap;
-  OSMetrics::MappedAndResidentPagesDumpState state =
-      OSMetrics::GetMappedAndResidentPages(base::android::kStartOfText,
-                                           base::android::kEndOfText,
-                                           &accessed_pages_bitmap);
-  UMA_HISTOGRAM_ENUMERATION(
-      "Memory.NativeLibrary.MappedAndResidentMemoryFootprintCollectionStatus",
-      state);
-
-  // MappedAndResidentPagesDumpState |state| can be |kAccessPagemapDenied|
-  // for Android devices running a kernel version < 4.4 or because the process
-  // is not "dumpable", as described in proc(5).
-  if (state != OSMetrics::MappedAndResidentPagesDumpState::kSuccess)
-    return state != OSMetrics::MappedAndResidentPagesDumpState::kFailure;
-
-  dump->native_library_pages_bitmap = std::move(accessed_pages_bitmap);
 #endif  // BUILDFLAG(SUPPORTS_CODE_ORDERING)
 #endif  //  BUILDFLAG(IS_ANDROID)
 
diff --git a/services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom b/services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom
index 4b189b1..22466be9 100644
--- a/services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom
+++ b/services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom
@@ -57,6 +57,8 @@
   // On Linux platforms, populate the pss_kb and swap_pss_kb fields of
   // RawOSMemDump.
   MEM_DUMP_PSS = 1,
+  // On Android, populate the native_library_pages_bitmap field of RawOSMemDump.
+  MEM_DUMP_PAGES_BITMAP = 2,
 };
 
 // These structs are internal only (only for the communication between
diff --git a/services/screen_ai/public/cpp/utilities.cc b/services/screen_ai/public/cpp/utilities.cc
index dfb440c..c5b062d 100644
--- a/services/screen_ai/public/cpp/utilities.cc
+++ b/services/screen_ai/public/cpp/utilities.cc
@@ -18,6 +18,9 @@
 
 namespace {
 
+// The maximum image dimension which is processed without downsampling by OCR.
+constexpr uint32_t kMaxImageDimensionForOcr = 2048;
+
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 constexpr char kBinaryPathSwitch[] = "screen-ai-binary";
 #endif
@@ -135,4 +138,8 @@
 #endif
 }
 
+uint32_t GetMaxDimensionForOCR() {
+  return kMaxImageDimensionForOcr;
+}
+
 }  // namespace screen_ai
diff --git a/services/screen_ai/public/cpp/utilities.h b/services/screen_ai/public/cpp/utilities.h
index 04e1b4b..6c380f2e 100644
--- a/services/screen_ai/public/cpp/utilities.h
+++ b/services/screen_ai/public/cpp/utilities.h
@@ -28,5 +28,13 @@
 // Returns the commandline switch for the binary file path.
 const char* GetBinaryPathSwitch();
 
+// Returns the maximum dimension for which images are processed without
+// downsampling. This value is not expected to change after initialization of
+// the service and is expected to be non-zero.
+// This value is best to be retrieved through
+// `ScreenAIAnnotator::GetMaxImageDimension` but as long the library does not
+// have dynamic maximum resolution setting, it is safe to use this value.
+uint32_t GetMaxDimensionForOCR();
+
 }  // namespace screen_ai
 #endif  // SERVICES_SCREEN_AI_PUBLIC_CPP_UTILITIES_H_
diff --git a/services/webnn/BUILD.gn b/services/webnn/BUILD.gn
index 9555d04b..2531b854 100644
--- a/services/webnn/BUILD.gn
+++ b/services/webnn/BUILD.gn
@@ -15,7 +15,6 @@
     "WEBNN_USE_TFLITE=$webnn_use_tflite",
     "WEBNN_USE_CHROME_ML_API=$webnn_use_chrome_ml_api",
     "WEBNN_ENABLE_TFLITE_PROFILER=$webnn_enable_tflite_profiler",
-    "WEBNN_USE_ORT=$webnn_use_ort",
   ]
 }
 
@@ -80,8 +79,8 @@
       "dml/command_recorder.h",
       "dml/context_impl_dml.cc",
       "dml/context_impl_dml.h",
-      "dml/context_provider.cc",
-      "dml/context_provider.h",
+      "dml/context_provider_dml.cc",
+      "dml/context_provider_dml.h",
       "dml/error.h",
       "dml/graph_builder_dml.cc",
       "dml/graph_builder_dml.h",
@@ -167,12 +166,18 @@
     }
   }
 
-  if (webnn_use_ort) {
+  if (is_win) {
     sources += [
+      "ort/context_impl_ort.cc",
+      "ort/context_impl_ort.h",
+      "ort/context_provider_ort.cc",
+      "ort/context_provider_ort.h",
       "ort/model_editor.cc",
       "ort/model_editor.h",
       "ort/ort_data_type.cc",
       "ort/ort_data_type.h",
+      "ort/ort_session_options.cc",
+      "ort/ort_session_options.h",
       "ort/ort_status.cc",
       "ort/ort_status.h",
       "ort/platform_functions_ort.cc",
@@ -225,7 +230,7 @@
     ]
   }
 
-  if (webnn_use_ort) {
+  if (is_win) {
     sources += [
       "ort/model_editor_test.cc",
       "ort/platform_functions_ort_test.cc",
diff --git a/services/webnn/dml/context_provider.cc b/services/webnn/dml/context_provider_dml.cc
similarity index 98%
rename from services/webnn/dml/context_provider.cc
rename to services/webnn/dml/context_provider_dml.cc
index 52d52227..a792d17 100644
--- a/services/webnn/dml/context_provider.cc
+++ b/services/webnn/dml/context_provider_dml.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/webnn/dml/context_provider.h"
+#include "services/webnn/dml/context_provider_dml.h"
 
 #include <wrl.h>
 
diff --git a/services/webnn/dml/context_provider.h b/services/webnn/dml/context_provider_dml.h
similarity index 88%
rename from services/webnn/dml/context_provider.h
rename to services/webnn/dml/context_provider_dml.h
index 7a45914..766484e0 100644
--- a/services/webnn/dml/context_provider.h
+++ b/services/webnn/dml/context_provider_dml.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_WEBNN_DML_CONTEXT_PROVIDER_H_
-#define SERVICES_WEBNN_DML_CONTEXT_PROVIDER_H_
+#ifndef SERVICES_WEBNN_DML_CONTEXT_PROVIDER_DML_H_
+#define SERVICES_WEBNN_DML_CONTEXT_PROVIDER_DML_H_
 
 #include "base/types/expected.h"
 #include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
@@ -39,4 +39,4 @@
 
 }  // namespace webnn
 
-#endif  // SERVICES_WEBNN_DML_CONTEXT_PROVIDER_H_
+#endif  // SERVICES_WEBNN_DML_CONTEXT_PROVIDER_DML_H_
diff --git a/services/webnn/features.gni b/services/webnn/features.gni
index 1537156..8d1bec4 100644
--- a/services/webnn/features.gni
+++ b/services/webnn/features.gni
@@ -16,7 +16,4 @@
 
   # Enable logging of TFLite profiling information on MLGraph destruction.
   webnn_enable_tflite_profiler = false
-
-  # Enable ONNX Runtime backend on Windows.
-  webnn_use_ort = is_win
 }
diff --git a/services/webnn/ort/context_impl_ort.cc b/services/webnn/ort/context_impl_ort.cc
new file mode 100644
index 0000000..d9d121f
--- /dev/null
+++ b/services/webnn/ort/context_impl_ort.cc
@@ -0,0 +1,202 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/webnn/ort/context_impl_ort.h"
+
+#include "base/notimplemented.h"
+#include "services/webnn/public/cpp/supported_data_types.h"
+#include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
+#include "services/webnn/public/mojom/webnn_graph.mojom.h"
+#include "services/webnn/public/mojom/webnn_tensor.mojom.h"
+#include "services/webnn/webnn_constant_operand.h"
+#include "services/webnn/webnn_context_impl.h"
+#include "services/webnn/webnn_graph_impl.h"
+
+namespace webnn::ort {
+
+ContextImplOrt::ContextImplOrt(
+    mojo::PendingReceiver<mojom::WebNNContext> receiver,
+    WebNNContextProviderImpl* context_provider,
+    mojom::CreateContextOptionsPtr options,
+    ScopedOrtEnv env,
+    scoped_refptr<SessionOptions> session_options)
+    : WebNNContextImpl(std::move(receiver),
+                       context_provider,
+                       GetContextProperties(),
+                       std::move(options)),
+      env_(std::move(env)),
+      session_options_(std::move(session_options)) {}
+
+ContextImplOrt::~ContextImplOrt() = default;
+
+// static
+ContextProperties ContextImplOrt::GetContextProperties() {
+  // TODO(crbug.com/412844034): Investigate how to set the tensor byte length
+  // limit and supported tensor ranks.
+  static constexpr uint64_t kTensorByteLengthLimit =
+      std::numeric_limits<int32_t>::max();
+
+  return ContextProperties(InputOperandLayout::kNchw,
+                           Resample2DAxes::kChannelsFirst,
+                           BatchNormalizationAxis::kChannelsFirst,
+                           /*tensor_byte_length_limit=*/kTensorByteLengthLimit,
+                           {/*input=*/SupportedDataTypes::All(),
+                            /*constant=*/SupportedDataTypes::All(),
+                            /*arg_min_max_input=*/
+                            {},
+                            /*arg_min_max_output=*/
+                            {},
+                            /*batch_normalization_input=*/{},
+                            /*batch_normalization_mean=*/{},
+                            /*cast_input=*/{},
+                            /*clamp_input=*/{},
+                            /*concat_inputs=*/{},
+                            /*conv2d_input=*/{},
+                            /*conv2d_bias=*/{},
+                            /*conv_transpose2d_input=*/{},
+                            /*conv_transpose2d_bias=*/{},
+                            /*cumulative_sum_input=*/{},
+                            /*dequantize_linear_input=*/{},
+                            /*dequantize_linear_scale=*/{},
+                            /*dequantize_linear_zero_point=*/{},
+                            /*add_input=*/{},
+                            /*sub_input=*/{},
+                            /*mul_input=*/{},
+                            /*div_input=*/{},
+                            /*max_input=*/{},
+                            /*min_input=*/{},
+                            /*pow_input=*/{},
+                            /*equal_input=*/{},
+                            /*greater_input=*/{},
+                            /*greater_or_equal_input=*/{},
+                            /*lesser_input=*/{},
+                            /*lesser_or_equal_input=*/{},
+                            /*not_equal_input=*/{},
+                            /*logical_and_input=*/{},
+                            /*logical_or_input=*/{},
+                            /*logical_xor_input=*/{},
+                            /*logical_not_input=*/{},
+                            /*logical_output=*/{},
+                            /*abs_input=*/{},
+                            /*ceil_input=*/{},
+                            /*cos_input=*/{},
+                            /*erf_input=*/{},
+                            /*exp_input=*/{},
+                            /*floor_input=*/{},
+                            /*identity_input=*/{},
+                            /*log_input=*/{},
+                            /*neg_input=*/{},
+                            /*reciprocal_input=*/
+                            {},
+                            /*sign_input=*/{},
+                            /*sin_input=*/{},
+                            /*sqrt_input=*/{},
+                            /*tan_input=*/{},
+                            /*elu_input=*/{},
+                            /*expand_input=*/{},
+                            /*gather_input=*/{},
+                            /*gather_indices=*/{},
+                            /*gather_elements_input=*/{},
+                            /*gather_elements_indices=*/{},
+                            /*gather_nd_input=*/{},
+                            /*gather_nd_indices=*/{},
+                            /*gelu_input=*/{},
+                            /*gemm_a=*/{},
+                            /*gemm_c=*/{},
+                            /*gru_input=*/{},
+                            /*gru_bias=*/{},
+                            /*gru_cell_input=*/{},
+                            /*gru_cell_bias=*/{},
+                            /*hard_sigmoid_input=*/
+                            {},
+                            /*hard_swish_input=*/
+                            {},
+                            /*instance_normalization_input=*/{},
+                            /*instance_normalization_scale=*/{},
+                            /*layer_normalization_input=*/{},
+                            /*leaky_relu_input=*/
+                            {},
+                            /*linear_input=*/{},
+                            /*lstm_input=*/{},
+                            /*lstm_bias=*/{},
+                            /*lstm_cell_input=*/{},
+                            /*lstm_cell_bias=*/{},
+                            /*matmul_input=*/{},
+                            /*pad_input=*/{},
+                            /*average_pool2d_input=*/
+                            {},
+                            /*l2_pool2d_input=*/{},
+                            /*max_pool2d_input=*/
+                            {},
+                            /*prelu_input=*/{},
+                            /*quantize_linear_input=*/{},
+                            /*quantize_linear_zero_point=*/{},
+                            /*reduce_l1_input=*/{},
+                            /*reduce_l2_input=*/{},
+                            /*reduce_log_sum_input=*/
+                            {},
+                            /*reduce_log_sum_exp_input=*/
+                            {},
+                            /*reduce_max_input=*/
+                            {},
+                            /*reduce_mean_input=*/
+                            {},
+                            /*reduce_min_input=*/
+                            {},
+                            /*reduce_product_input=*/
+                            {},
+                            /*reduce_sum_input=*/
+                            {},
+                            /*reduce_sum_square_input=*/
+                            {},
+                            /*relu_input=*/{},
+                            /*resample2d_input=*/
+                            {},
+                            /*reshape_input=*/{},
+                            /*reverse_input=*/{},
+                            /*scatter_elements_input=*/{},
+                            /*scatter_elements_indices=*/{},
+                            /*scatter_nd_input=*/{},
+                            /*scatter_nd_indices=*/{},
+                            /*scatter_nd_updates=*/{},
+                            /*sigmoid_input=*/{},
+                            /*slice_input=*/{},
+                            /*softmax_input=*/{},
+                            /*softplus_input=*/{},
+                            /*softsign_input=*/{},
+                            /*split_input=*/{},
+                            /*tanh_input=*/{},
+                            /*tile_input=*/{},
+                            /*transpose_input=*/{},
+                            /*triangular_input=*/
+                            {},
+                            /*where_condition=*/{},
+                            /*where_value=*/{}});
+}
+
+base::WeakPtr<WebNNContextImpl> ContextImplOrt::AsWeakPtr() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return weak_factory_.GetWeakPtr();
+}
+
+void ContextImplOrt::CreateGraphImpl(
+    mojo::PendingAssociatedReceiver<mojom::WebNNGraph> receiver,
+    mojom::GraphInfoPtr graph_info,
+    WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
+    base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
+        constant_operands,
+    CreateGraphImplCallback callback) {
+  // TODO(crbug.com/416535744): Implement GraphImpl for ORT backend.
+  NOTIMPLEMENTED();
+}
+
+void ContextImplOrt::CreateTensorImpl(
+    mojo::PendingAssociatedReceiver<mojom::WebNNTensor> receiver,
+    mojom::TensorInfoPtr tensor_info,
+    CreateTensorImplCallback callback) {
+  // TODO(crbug.com/416539419): Implement TensorImpl for ORT backend.
+  NOTIMPLEMENTED();
+}
+
+}  // namespace webnn::ort
diff --git a/services/webnn/ort/context_impl_ort.h b/services/webnn/ort/context_impl_ort.h
new file mode 100644
index 0000000..cab751f6
--- /dev/null
+++ b/services/webnn/ort/context_impl_ort.h
@@ -0,0 +1,72 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_WEBNN_ORT_CONTEXT_IMPL_ORT_H_
+#define SERVICES_WEBNN_ORT_CONTEXT_IMPL_ORT_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "services/webnn/ort/ort_session_options.h"
+#include "services/webnn/ort/scoped_ort_types.h"
+#include "services/webnn/public/cpp/webnn_types.h"
+#include "services/webnn/webnn_context_impl.h"
+
+namespace webnn {
+
+class WebNNConstantOperand;
+
+namespace ort {
+
+// `ContextImplOrt` is created by `WebNNContextProviderImpl` and responsible
+// for creating a `GraphImplOrt` which uses ONNX Runtime for inference.
+class ContextImplOrt final : public WebNNContextImpl {
+ public:
+  ContextImplOrt(mojo::PendingReceiver<mojom::WebNNContext> receiver,
+                 WebNNContextProviderImpl* context_provider,
+                 mojom::CreateContextOptionsPtr options,
+                 ScopedOrtEnv env,
+                 scoped_refptr<SessionOptions> session_options);
+
+  ContextImplOrt(const WebNNContextImpl&) = delete;
+  ContextImplOrt& operator=(const ContextImplOrt&) = delete;
+
+  ~ContextImplOrt() override;
+
+  // WebNNContextImpl:
+  base::WeakPtr<WebNNContextImpl> AsWeakPtr() override;
+
+  static ContextProperties GetContextProperties();
+
+  scoped_refptr<SessionOptions> session_options() const {
+    return session_options_;
+  }
+
+ private:
+  void CreateGraphImpl(
+      mojo::PendingAssociatedReceiver<mojom::WebNNGraph> receiver,
+      mojom::GraphInfoPtr graph_info,
+      WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
+      base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
+          constant_operands,
+      CreateGraphImplCallback callback) override;
+
+  void CreateTensorImpl(
+      mojo::PendingAssociatedReceiver<mojom::WebNNTensor> receiver,
+      mojom::TensorInfoPtr tensor_info,
+      CreateTensorImplCallback callback) override;
+
+  // It is sequence bound.
+  ScopedOrtEnv env_;
+
+  // The session options are shared among all the sessions created by this
+  // context.
+  scoped_refptr<SessionOptions> session_options_;
+
+  base::WeakPtrFactory<ContextImplOrt> weak_factory_{this};
+};
+
+}  // namespace ort
+}  // namespace webnn
+
+#endif  // SERVICES_WEBNN_ORT_CONTEXT_IMPL_ORT_H_
diff --git a/services/webnn/ort/context_provider_ort.cc b/services/webnn/ort/context_provider_ort.cc
new file mode 100644
index 0000000..09e7723
--- /dev/null
+++ b/services/webnn/ort/context_provider_ort.cc
@@ -0,0 +1,81 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/webnn/ort/context_provider_ort.h"
+
+#include "base/command_line.h"
+#include "base/types/expected_macros.h"
+#include "services/webnn/ort/context_impl_ort.h"
+#include "services/webnn/ort/ort_status.h"
+#include "services/webnn/ort/platform_functions_ort.h"
+#include "services/webnn/ort/scoped_ort_types.h"
+#include "services/webnn/webnn_switches.h"
+
+namespace webnn::ort {
+
+namespace {
+
+// Helper function to convert a string to OrtLoggingLevel enum.
+OrtLoggingLevel StringToOrtLoggingLevel(std::string_view logging_level) {
+  if (logging_level == "VERBOSE") {
+    return ORT_LOGGING_LEVEL_VERBOSE;
+  } else if (logging_level == "INFO") {
+    return ORT_LOGGING_LEVEL_INFO;
+  } else if (logging_level == "WARNING") {
+    return ORT_LOGGING_LEVEL_WARNING;
+  } else if (logging_level == "ERROR") {
+    return ORT_LOGGING_LEVEL_ERROR;
+  } else if (logging_level == "FATAL") {
+    return ORT_LOGGING_LEVEL_FATAL;
+  }
+  // Default to ERROR if the input is invalid.
+  LOG(WARNING) << "[WebNN] Unrecognized logging level: " << logging_level
+               << ". Default ERROR level will be used.";
+  return ORT_LOGGING_LEVEL_ERROR;
+}
+
+}  // namespace
+
+base::expected<std::unique_ptr<WebNNContextImpl>, mojom::ErrorPtr>
+CreateContextFromOptions(mojom::CreateContextOptionsPtr options,
+                         mojo::PendingReceiver<mojom::WebNNContext> receiver,
+                         WebNNContextProviderImpl* context_provider) {
+  auto* platform_functions = PlatformFunctions::GetInstance();
+  if (!platform_functions) {
+    return base::unexpected(mojom::Error::New(
+        mojom::Error::Code::kNotSupportedError,
+        "WebNN is not supported in this ONNX Runtime version."));
+  }
+
+  OrtLoggingLevel ort_logging_level = ORT_LOGGING_LEVEL_ERROR;
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kWebNNOrtLoggingLevel)) {
+    std::string user_logging_level =
+        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+            switches::kWebNNOrtLoggingLevel);
+    ort_logging_level = StringToOrtLoggingLevel(user_logging_level);
+  }
+
+  // `OrtEnv` is reference counted. The first `CreateEnv()` will create the
+  // `OrtEnv` instance. The following invocations return a reference to the
+  // same instance. It is released upon the last reference is removed via
+  // `ReleaseEnv()`.
+  // Creating and releasing `OrtEnv` are protected by a lock internally, so it
+  // is sequence bound.
+  ScopedOrtEnv env;
+  if (ORT_CALL_FAILED(platform_functions->ort_api()->CreateEnv(
+          ort_logging_level, "WebNN", ScopedOrtEnv::Receiver(env).get()))) {
+    return base::unexpected(
+        mojom::Error::New(mojom::Error::Code::kNotSupportedError,
+                          "Failed to create the ONNX Runtime environment."));
+  }
+
+  ASSIGN_OR_RETURN(scoped_refptr<SessionOptions> session_options,
+                   SessionOptions::Create(options->device));
+  return std::make_unique<ContextImplOrt>(std::move(receiver), context_provider,
+                                          std::move(options), std::move(env),
+                                          std::move(session_options));
+}
+
+}  // namespace webnn::ort
diff --git a/services/webnn/dml/context_provider.h b/services/webnn/ort/context_provider_ort.h
similarity index 60%
copy from services/webnn/dml/context_provider.h
copy to services/webnn/ort/context_provider_ort.h
index 7a45914..9ffb4e52 100644
--- a/services/webnn/dml/context_provider.h
+++ b/services/webnn/ort/context_provider_ort.h
@@ -2,41 +2,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_WEBNN_DML_CONTEXT_PROVIDER_H_
-#define SERVICES_WEBNN_DML_CONTEXT_PROVIDER_H_
+#ifndef SERVICES_WEBNN_ORT_CONTEXT_PROVIDER_ORT_H_
+#define SERVICES_WEBNN_ORT_CONTEXT_PROVIDER_ORT_H_
 
 #include "base/types/expected.h"
 #include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
 #include "services/webnn/public/mojom/webnn_error.mojom.h"
 
-namespace gpu {
-class SharedContextState;
-struct GpuFeatureInfo;
-struct GPUInfo;
-}  // namespace gpu
-
 namespace webnn {
 
 class WebNNContextImpl;
 class WebNNContextProviderImpl;
 
-namespace dml {
-
-bool ShouldCreateDmlContext(const mojom::CreateContextOptions& options);
+namespace ort {
 
 // Create a WebNN context that satisfies the requested preferences in a
 // CreateContextOptions. This corresponds to the
 // ML.createContext(MLContextOptions) overload in the WebNN API.
 base::expected<std::unique_ptr<WebNNContextImpl>, mojom::ErrorPtr>
 CreateContextFromOptions(mojom::CreateContextOptionsPtr options,
-                         const gpu::GpuFeatureInfo& gpu_feature_info,
-                         const gpu::GPUInfo& gpu_info,
-                         const gpu::SharedContextState* shared_context_state,
                          mojo::PendingReceiver<mojom::WebNNContext> receiver,
                          WebNNContextProviderImpl* context_provider);
 
-}  // namespace dml
+}  // namespace ort
 
 }  // namespace webnn
 
-#endif  // SERVICES_WEBNN_DML_CONTEXT_PROVIDER_H_
+#endif  // SERVICES_WEBNN_ORT_CONTEXT_PROVIDER_ORT_H_
diff --git a/services/webnn/ort/ort_session_options.cc b/services/webnn/ort/ort_session_options.cc
new file mode 100644
index 0000000..62e8916
--- /dev/null
+++ b/services/webnn/ort/ort_session_options.cc
@@ -0,0 +1,66 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/webnn/ort/ort_session_options.h"
+
+#include "services/webnn/ort/ort_status.h"
+#include "services/webnn/ort/platform_functions_ort.h"
+#include "services/webnn/public/cpp/webnn_trace.h"
+#include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
+#include "services/webnn/public/mojom/webnn_error.mojom.h"
+#include "third_party/onnxruntime_headers/src/include/onnxruntime/core/session/onnxruntime_session_options_config_keys.h"
+
+namespace webnn::ort {
+
+// static
+base::expected<scoped_refptr<SessionOptions>, mojom::ErrorPtr>
+SessionOptions::Create(mojom::CreateContextOptions::Device device_type) {
+  ScopedTrace scoped_trace("SessionOptions::Create");
+
+  if (device_type != mojom::CreateContextOptions::Device::kCpu) {
+    return base::unexpected(mojom::Error::New(
+        mojom::Error::Code::kNotSupportedError,
+        "The ONNX Runtime backend only supports CPU device type currently."));
+  }
+
+  scoped_trace.AddStep("Create session options");
+  const OrtApi* ort_api = PlatformFunctions::GetInstance()->ort_api();
+  ScopedOrtSessionOptions session_options;
+  CHECK_STATUS(ort_api->CreateSessionOptions(
+      ScopedOrtSessionOptions::Receiver(session_options).get()));
+  // TODO(crbug.com/416539420): Add a switch to dump model once ORT backend can
+  // build a model.
+
+  // Enable strict shape type inference check. All inconsistencies encountered
+  // will expose errors during session creation. For example, if the graph
+  // output shape set by WebNN is different from ONNX shape inference result,
+  // the session creation will fail.
+  CHECK_STATUS(ort_api->AddSessionConfigEntry(
+      session_options.get(),
+      /*config_key=*/kOrtSessionOptionsConfigStrictShapeTypeInference,
+      /*config_value=*/"1"));
+
+  // Use CPU EP by default.
+  //
+  // TODO(crbug.com/412841630): Investigate how to apply layout optimizations
+  // (ORT_ENABLE_ALL).
+  // https://onnxruntime.ai/docs/performance/model-optimizations/graph-optimizations.html#layout-optimizations
+  // TODO(crbug.com/416543902): Add a switch to test different optimization
+  // levels at runtime.
+  CHECK_STATUS(ort_api->SetSessionGraphOptimizationLevel(
+      session_options.get(), GraphOptimizationLevel::ORT_ENABLE_BASIC));
+
+  return base::MakeRefCounted<SessionOptions>(base::PassKey<SessionOptions>(),
+                                              std::move(session_options));
+}
+
+SessionOptions::SessionOptions(base::PassKey<SessionOptions>,
+                               ScopedOrtSessionOptions session_options)
+    : session_options_(std::move(session_options)) {
+  CHECK(session_options_.get());
+}
+
+SessionOptions::~SessionOptions() = default;
+
+}  // namespace webnn::ort
diff --git a/services/webnn/ort/ort_session_options.h b/services/webnn/ort/ort_session_options.h
new file mode 100644
index 0000000..d27267c
--- /dev/null
+++ b/services/webnn/ort/ort_session_options.h
@@ -0,0 +1,45 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_WEBNN_ORT_ORT_SESSION_OPTIONS_H_
+#define SERVICES_WEBNN_ORT_ORT_SESSION_OPTIONS_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/types/expected.h"
+#include "base/types/pass_key.h"
+#include "services/webnn/ort/scoped_ort_types.h"
+#include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
+#include "third_party/onnxruntime_headers/src/include/onnxruntime/core/session/onnxruntime_c_api.h"
+
+namespace webnn::ort {
+
+// `SessionOptions` is a wrapper of `OrtSessionOptions` and used to create
+// sessions on background threads.
+class SessionOptions final : public base::RefCountedThreadSafe<SessionOptions> {
+ public:
+  // The `device_type` would be used to configure ONNX Runtime EP. Currently,
+  // only CPU is supported by the default CPU EP.
+  // It may fail when appending a particular EP to the session options.
+  static base::expected<scoped_refptr<SessionOptions>, mojom::ErrorPtr> Create(
+      mojom::CreateContextOptions::Device device_type);
+
+  SessionOptions(base::PassKey<SessionOptions>,
+                 ScopedOrtSessionOptions session_options);
+
+  SessionOptions(const SessionOptions&) = delete;
+  SessionOptions& operator=(const SessionOptions&) = delete;
+
+  const OrtSessionOptions* get() const { return session_options_.get(); }
+
+ private:
+  friend class base::RefCountedThreadSafe<SessionOptions>;
+
+  ~SessionOptions();
+
+  ScopedOrtSessionOptions session_options_;
+};
+
+}  // namespace webnn::ort
+
+#endif  // SERVICES_WEBNN_ORT_ORT_SESSION_OPTIONS_H_
diff --git a/services/webnn/ort/ort_status.cc b/services/webnn/ort/ort_status.cc
index 2182761..28565ea 100644
--- a/services/webnn/ort/ort_status.cc
+++ b/services/webnn/ort/ort_status.cc
@@ -13,7 +13,7 @@
 
 namespace internal {
 
-std::string OrtStatusFatalMessage(OrtStatus* status) {
+std::string OrtStatusErrorMessage(OrtStatus* status) {
   CHECK(status);
 
   constexpr char kOrtErrorCode[] = "[WebNN] ORT status error code: ";
diff --git a/services/webnn/ort/ort_status.h b/services/webnn/ort/ort_status.h
index ce27817..eb9248da 100644
--- a/services/webnn/ort/ort_status.h
+++ b/services/webnn/ort/ort_status.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/logging.h"
+#include "services/webnn/ort/scoped_ort_types.h"
 
 struct OrtStatus;
 
@@ -15,15 +16,34 @@
 
 namespace internal {
 
-std::string OrtStatusFatalMessage(OrtStatus* status);
+std::string OrtStatusErrorMessage(OrtStatus* status);
 
 }  // namespace internal
 
-#define CHECK_STATUS(expr)                                 \
-  if (OrtStatus* status = (expr)) {                        \
-    LOG(FATAL) << internal::OrtStatusFatalMessage(status); \
+#define CHECK_STATUS(expr)                                      \
+  if (OrtStatus* status = (expr)) {                             \
+    LOG(FATAL) << ort::internal::OrtStatusErrorMessage(status); \
   }
 
+#define CALL_ORT_FUNC(expr)                                             \
+  ([&]() -> ort::ScopedOrtStatus {                                      \
+    ort::ScopedOrtStatus status(expr);                                  \
+    if (status.is_valid()) {                                            \
+      LOG(ERROR) << "[WebNN] Failed to call " << #expr << ": "          \
+                 << ort::internal::OrtStatusErrorMessage(status.get()); \
+    }                                                                   \
+    return status;                                                      \
+  })()
+
+#define ORT_CALL_FAILED(expr)                         \
+  ([&]() -> bool {                                    \
+    ort::ScopedOrtStatus status(CALL_ORT_FUNC(expr)); \
+    if (status.is_valid()) {                          \
+      return true;                                    \
+    }                                                 \
+    return false;                                     \
+  })()
+
 }  // namespace webnn::ort
 
 #endif  // SERVICES_WEBNN_ORT_ORT_STATUS_H_
diff --git a/services/webnn/ort/scoped_ort_types.h b/services/webnn/ort/scoped_ort_types.h
index 3b75221b..aad9423 100644
--- a/services/webnn/ort/scoped_ort_types.h
+++ b/services/webnn/ort/scoped_ort_types.h
@@ -27,6 +27,27 @@
 };
 
 template <>
+struct ScopedOrtTypeTraitsHelper<OrtEnv*> {
+  static void Free(OrtEnv* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseEnv(value);
+  }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtSessionOptions*> {
+  static void Free(OrtSessionOptions* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseSessionOptions(value);
+  }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtStatus*> {
+  static void Free(OrtStatus* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseStatus(value);
+  }
+};
+
+template <>
 struct ScopedOrtTypeTraitsHelper<OrtValue*> {
   static void Free(OrtValue* value) {
     PlatformFunctions::GetInstance()->ort_api()->ReleaseValue(value);
@@ -95,6 +116,9 @@
 
 }  // namespace internal
 
+using ScopedOrtEnv = internal::ScopedOrtType<OrtEnv>;
+using ScopedOrtSessionOptions = internal::ScopedOrtType<OrtSessionOptions>;
+using ScopedOrtStatus = internal::ScopedOrtType<OrtStatus>;
 using ScopedOrtValue = internal::ScopedOrtType<OrtValue>;
 using ScopedOrtMemoryInfo = internal::ScopedOrtType<OrtMemoryInfo>;
 using ScopedOrtOpAttr = internal::ScopedOrtType<OrtOpAttr>;
diff --git a/services/webnn/public/mojom/features.mojom b/services/webnn/public/mojom/features.mojom
index d43990af6..4a431fb9 100644
--- a/services/webnn/public/mojom/features.mojom
+++ b/services/webnn/public/mojom/features.mojom
@@ -39,3 +39,10 @@
   const string name = "WebNNDirectML";
   const bool default_state = true;
 };
+
+// Enables the ONNX Runtime backend for WebNN.
+[EnableIf=is_win]
+feature kWebNNOnnxRuntime {
+  const string name = "WebNNOnnxRuntime";
+  const bool default_state = false;
+};
diff --git a/services/webnn/tflite/graph_builder_tflite.cc b/services/webnn/tflite/graph_builder_tflite.cc
index 626df2944..8693b74 100644
--- a/services/webnn/tflite/graph_builder_tflite.cc
+++ b/services/webnn/tflite/graph_builder_tflite.cc
@@ -1822,6 +1822,58 @@
   return SerializeQuantizedOutput(*next_op);
 }
 
+std::optional<GraphBuilderTflite::TensorInfo>
+GraphBuilderTflite::CanFuseQuantizeAndGetOutput(const mojom::Softmax& softmax) {
+  if (!IsDequantizeOutput(softmax.input_operand_id)) {
+    return std::nullopt;
+  }
+
+  // TODO(crbug.com/413083273): Consider the restriction in GPU delegate.
+  // For TFLite kernel, the scale of output should be approximately equal
+  // to 1.0f / 256.0f and the zero point of output should be equal to -128
+  // if data type is int8.
+  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/tflite/src/tensorflow/lite/kernels/activations.cc;l=541;drc=f667feb8a5c6f227b49328ce78a062acc4f81187
+  const mojom::DequantizeLinear& input_dequantize =
+      GetDequantizeOp(softmax.input_operand_id);
+  if (!IsInts8AndScalarScale(input_dequantize)) {
+    return std::nullopt;
+  }
+
+  const OperandDataType quantized_type =
+      GetOperand(input_dequantize.input_operand_id).descriptor.data_type();
+  std::optional<std::pair<OperationId, QuantizateParametersOffset>> next_op =
+      IsNextOpQuantize(softmax.output_operand_id, {quantized_type});
+  if (!next_op) {
+    return std::nullopt;
+  }
+
+  const mojom::QuantizeLinear& output_quantize = GetQuantizeOp(next_op->first);
+  if (!IsInts8AndScalarScale(output_quantize)) {
+    return std::nullopt;
+  }
+
+  if (quantized_type == OperandDataType::kInt8) {
+    const float expected_scale_value = 1.0f / 256.0f;
+    base::span<const float> output_scale_values =
+        GetConstantValue<float>(output_quantize.scale_operand_id);
+    base::CheckedNumeric<float> checked_scale =
+        base::MakeCheckedNum<float>(output_scale_values[0]) -
+        expected_scale_value;
+    if (!checked_scale.IsValid() ||
+        checked_scale.Abs().ValueOrDie() > 0.001f * expected_scale_value) {
+      return std::nullopt;
+    }
+
+    base::FixedArray<int64_t> output_zero_point_values =
+        GetConstantInt64Value(output_quantize.zero_point_operand_id);
+    if (output_zero_point_values[0] != -128) {
+      return std::nullopt;
+    }
+  }
+
+  return SerializeQuantizedOutput(*next_op);
+}
+
 std::optional<base::FixedArray<GraphBuilderTflite::TensorInfo>>
 GraphBuilderTflite::CanFuseQuantizeAndGetOutput(const mojom::Split& split) {
   if (!IsDequantizeOutput(split.input_operand_id)) {
@@ -6615,10 +6667,18 @@
     -> base::expected<OperatorOffset, std::string> {
   CHECK(context_properties_.data_type_limits.softmax_input.Supports(
       GetOperand(softmax.input_operand_id).descriptor));
+
+  std::optional<TensorInfo> quantized_output =
+      CanFuseQuantizeAndGetOutput(softmax);
+  const bool fuse_dequantize = quantized_output.has_value();
   ASSIGN_OR_RETURN(const TensorInfo& input_tensor_info,
-                   SerializeInputTensorInfo(softmax.input_operand_id));
+                   SerializeInputTensorInfo(
+                       softmax.input_operand_id, /*quantize_params=*/0,
+                       /*operation_supports_float16=*/false, fuse_dequantize));
   const TensorIndex output_tensor_index =
-      SerializeOutputTensorInfo(softmax.output_operand_id).index;
+      fuse_dequantize
+          ? quantized_output->index
+          : SerializeOutputTensorInfo(softmax.output_operand_id).index;
 
   const size_t input_rank = input_tensor_info.dimensions.size();
   const auto softmax_options =
@@ -6639,15 +6699,24 @@
   std::swap(transpose_dimensions[softmax.axis],
             transpose_dimensions[input_rank - 1]);
 
+  ::tflite::TensorType data_type = input_tensor_info.data_type;
+  QuantizateParametersOffset input_quantize_params = 0;
+  QuantizateParametersOffset output_quantize_params = 0;
+  if (fuse_dequantize) {
+    data_type = quantized_output->data_type;
+    input_quantize_params = input_tensor_info.quantize_params;
+    output_quantize_params = quantized_output->quantize_params;
+  }
+
   const TensorIndex output_tensor_index_of_transpose = SerializeTemporaryTensor(
-      transpose_dimensions, input_tensor_info.data_type);
+      transpose_dimensions, data_type, input_quantize_params);
   operators_.emplace_back(SerializeTransposeOperation(
       input_tensor_info.index, output_tensor_index_of_transpose,
       input_tensor_info.dimensions, permutation));
 
   // Perform softmax.
   const TensorIndex output_tensor_index_of_softmax = SerializeTemporaryTensor(
-      input_tensor_info.dimensions, input_tensor_info.data_type);
+      transpose_dimensions, data_type, output_quantize_params);
   operators_.emplace_back(SerializeUnaryOperation(
       ::tflite::BuiltinOperator_SOFTMAX, output_tensor_index_of_transpose,
       output_tensor_index_of_softmax, ::tflite::BuiltinOptions_SoftmaxOptions,
diff --git a/services/webnn/tflite/graph_builder_tflite.h b/services/webnn/tflite/graph_builder_tflite.h
index 3f866f09..1d0653c 100644
--- a/services/webnn/tflite/graph_builder_tflite.h
+++ b/services/webnn/tflite/graph_builder_tflite.h
@@ -728,6 +728,8 @@
       const mojom::Reshape& reshape);
   std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(
       const mojom::Slice& slice);
+  std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(
+      const mojom::Softmax& softmax);
   std::optional<base::FixedArray<TensorInfo>> CanFuseQuantizeAndGetOutput(
       const mojom::Split& split);
   std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(
diff --git a/services/webnn/webnn_context_provider_impl.cc b/services/webnn/webnn_context_provider_impl.cc
index ff2dcda..1db3f50 100644
--- a/services/webnn/webnn_context_provider_impl.cc
+++ b/services/webnn/webnn_context_provider_impl.cc
@@ -19,7 +19,10 @@
 #include "services/webnn/webnn_context_impl.h"
 
 #if BUILDFLAG(IS_WIN)
-#include "services/webnn/dml/context_provider.h"
+#include "base/types/expected_macros.h"
+#include "services/webnn/dml/context_provider_dml.h"
+#include "services/webnn/ort/context_provider_ort.h"
+#include "services/webnn/ort/ort_session_options.h"
 #endif
 
 #if BUILDFLAG(IS_MAC)
@@ -177,8 +180,22 @@
   RecordDeviceType(options->device);
 
 #if BUILDFLAG(IS_WIN)
-  if (dml::ShouldCreateDmlContext(*options)) {
-    auto context_creation_results = dml::CreateContextFromOptions(
+  base::expected<std::unique_ptr<WebNNContextImpl>, mojom::ErrorPtr>
+      context_creation_results;
+
+  if (base::FeatureList::IsEnabled(mojom::features::kWebNNOnnxRuntime)) {
+    context_creation_results = ort::CreateContextFromOptions(
+        std::move(options), std::move(receiver), this);
+    if (!context_creation_results.has_value()) {
+      std::move(callback).Run(mojom::CreateContextResult::NewError(
+          std::move(context_creation_results.error())));
+      return;
+    }
+    context_impl = std::move(context_creation_results.value());
+  }
+
+  if (!context_impl && dml::ShouldCreateDmlContext(*options)) {
+    context_creation_results = dml::CreateContextFromOptions(
         std::move(options), gpu_feature_info_, gpu_info_,
         shared_context_state_.get(), std::move(receiver), this);
     if (!context_creation_results.has_value()) {
diff --git a/services/webnn/webnn_switches.h b/services/webnn/webnn_switches.h
index ce6bebc..e7773582 100644
--- a/services/webnn/webnn_switches.h
+++ b/services/webnn/webnn_switches.h
@@ -26,6 +26,14 @@
 inline constexpr char kWebNNTfliteDumpModel[] = "webnn-tflite-dump-model";
 #endif  // BUILDFLAG(WEBNN_USE_TFLITE)
 
+#if BUILDFLAG(IS_WIN)
+// Configure the logging severity level of ONNX Runtime.
+// Usage: --no-sandbox --enable-logging --webnn-ort-logging-level=VERBOSE
+// Other severity levels could be "INFO", "WARNING", "ERROR" (default), and
+// "FATAL".
+inline constexpr char kWebNNOrtLoggingLevel[] = "webnn-ort-logging-level";
+#endif  // BUILDFLAG(IS_WIN)
+
 }  // namespace switches
 
 #endif  // SERVICES_WEBNN_WEBNN_SWITCHES_H_
diff --git a/testing/buildbot/filters/accessibility-linux.interactive_ui_tests.filter b/testing/buildbot/filters/accessibility-linux.interactive_ui_tests.filter
index 7552e42..9558b1af 100644
--- a/testing/buildbot/filters/accessibility-linux.interactive_ui_tests.filter
+++ b/testing/buildbot/filters/accessibility-linux.interactive_ui_tests.filter
@@ -3,14 +3,11 @@
 # --force-renderer-accessibility is used.
 -CaptionBubbleControllerViewsTest.AccessibleTextIsSometimesFocusable
 
-# This test crashes due to DCHECK failed: ax_tree_id != AXTreeIDUnknown()
--AutofillInteractiveTest/AutofillInteractiveFencedFrameTest.DeletingFrameClosesPopup/1
-
-# This test times out, but only if accessibility is enabled.
+# These tests fail because WebUITabStripContainerView is nullptr.
+# The reason why is that WebUITabStripContainerView::UseTouchableTabStrip()
+# returns false when a screen reader is active. Until that changes, these
+# tests are irrelevant on this bot.
 -FeaturePromoDialogWebUITabStripTest.InvokeUi_IPH_WebUITabStrip
-
-# These tests fail because WebUITabStripContainerView is nullptr,
-# but only if accessibility is enabled.
 -WebUITabStripInteractiveTest.CanTypeInOmniboxAfterTabStripClose
 -WebUITabStripInteractiveTest.EventInContainerDoesNotClose
 -WebUITabStripInteractiveTest.EventInTabContentClosesContainer
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a436e0e..26c6273 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2026,6 +2026,7 @@
         {
             "platforms": [
                 "android",
+                "ios",
                 "mac",
                 "windows"
             ],
@@ -10636,6 +10637,24 @@
             ]
         }
     ],
+    "HTMLParserYieldByUserTiming": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "EnableWithTimeout5ms",
+                    "params": {
+                        "timeout_ms": "5"
+                    },
+                    "enable_features": [
+                        "SearchAboveFoldBytesReceived"
+                    ]
+                }
+            ]
+        }
+    ],
     "HTTP2": [
         {
             "platforms": [
@@ -11147,16 +11166,25 @@
             ]
         }
     ],
-    "HeapProfilerExcludeChromeOSRenderers": [
+    "HeapProfilingLoadFactor": [
         {
             "platforms": [
-                "chromeos"
+                "android",
+                "chromeos",
+                "ios",
+                "linux",
+                "mac",
+                "windows"
             ],
             "experiments": [
                 {
                     "name": "Enabled",
                     "params": {
-                        "renderer-prob-pct": "0"
+                        "browser-hash-set-load-factor": "0.7",
+                        "gpu-hash-set-load-factor": "0.7",
+                        "network-hash-set-load-factor": "0.7",
+                        "renderer-hash-set-load-factor": "0.7",
+                        "utility-hash-set-load-factor": "0.7"
                     },
                     "enable_features": [
                         "HeapProfilerReporting"
@@ -13531,6 +13559,24 @@
             ]
         }
     ],
+    "LensSearchSidePanelScrollToAPI": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "LensSearchSidePanelScrollToAPIEnabled",
+                    "enable_features": [
+                        "LensSearchSidePanelScrollToAPI"
+                    ]
+                }
+            ]
+        }
+    ],
     "LinkCrossDeviceDogFoodFeedback": [
         {
             "platforms": [
@@ -16913,14 +16959,14 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled_Uniform_20250429",
+                    "name": "Enabled_Uniform_20250520",
                     "params": {
                         "en_site_id": "N5wFxEDQr0ugnJ3q1cK0SNopqcEc",
                         "hats_histogram_name": "Feedback.HappinessTrackingSurvey.PerformanceControlsPPMSurvey",
                         "hats_survey_ukm_id": "1027171324",
                         "ppm_survey_segment_name1": "ChromeOS",
                         "ppm_survey_uniform_sample": "true",
-                        "probability": "0.088",
+                        "probability": "0.108",
                         "survey": "performance-ppm"
                     },
                     "enable_features": [
@@ -16935,14 +16981,14 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled_Uniform_20250429",
+                    "name": "Enabled_Uniform_20250520",
                     "params": {
                         "en_site_id": "N5wFxEDQr0ugnJ3q1cK0SNopqcEc",
                         "hats_histogram_name": "Feedback.HappinessTrackingSurvey.PerformanceControlsPPMSurvey",
                         "hats_survey_ukm_id": "1027171324",
                         "ppm_survey_segment_name1": "Linux",
                         "ppm_survey_uniform_sample": "true",
-                        "probability": "0.088",
+                        "probability": "1.0",
                         "survey": "performance-ppm"
                     },
                     "enable_features": [
@@ -16957,7 +17003,7 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled_Uniform_20250429",
+                    "name": "Enabled_Uniform_20250520",
                     "params": {
                         "en_site_id": "N5wFxEDQr0ugnJ3q1cK0SNopqcEc",
                         "hats_histogram_name": "Feedback.HappinessTrackingSurvey.PerformanceControlsPPMSurvey",
@@ -16966,7 +17012,7 @@
                         "ppm_survey_segment_name1": "Mac, up to 8 GB",
                         "ppm_survey_segment_name2": "Mac, over 8 GB",
                         "ppm_survey_uniform_sample": "true",
-                        "probability": "0.088",
+                        "probability": "0.252",
                         "survey": "performance-ppm"
                     },
                     "enable_features": [
@@ -16981,7 +17027,7 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled_Uniform_20250429",
+                    "name": "Enabled_Uniform_20250520",
                     "params": {
                         "en_site_id": "N5wFxEDQr0ugnJ3q1cK0SNopqcEc",
                         "hats_histogram_name": "Feedback.HappinessTrackingSurvey.PerformanceControlsPPMSurvey",
@@ -16989,8 +17035,8 @@
                         "ppm_survey_segment_max_memory_gb1": "4",
                         "ppm_survey_segment_max_memory_gb2": "8",
                         "ppm_survey_segment_name1": "Windows, up to 4 GB",
-                        "ppm_survey_segment_name2": "Windows, 4-8 GB",
-                        "ppm_survey_segment_name3": "Windows, over 8 GB",
+                        "ppm_survey_segment_name2": "",
+                        "ppm_survey_segment_name3": "",
                         "ppm_survey_uniform_sample": "true",
                         "probability": "0.088",
                         "survey": "performance-ppm"
@@ -17705,6 +17751,9 @@
             "experiments": [
                 {
                     "name": "Enabled",
+                    "params": {
+                        "kPrefetchSchedulerProgressSyncBestEffort": "true"
+                    },
                     "enable_features": [
                         "PrefetchScheduler"
                     ]
diff --git a/third_party/android_deps/autorolled/VERSION.txt b/third_party/android_deps/autorolled/VERSION.txt
index 21b02bf..db78f11 100644
--- a/third_party/android_deps/autorolled/VERSION.txt
+++ b/third_party/android_deps/autorolled/VERSION.txt
@@ -1 +1 @@
-9cf783fd9ae7fea.61e2bf31c2a1d83
\ No newline at end of file
+8080a84ade58629.cb2f7ad9f1b5de0
\ No newline at end of file
diff --git a/third_party/android_deps/autorolled/bill_of_materials.json b/third_party/android_deps/autorolled/bill_of_materials.json
index c14e477..9c30a556 100644
--- a/third_party/android_deps/autorolled/bill_of_materials.json
+++ b/third_party/android_deps/autorolled/bill_of_materials.json
@@ -2,7 +2,7 @@
     {
         "name": "activity",
         "group": "androidx.activity",
-        "version": "1.12.0-SNAPSHOT"
+        "version": "1.12.0-alpha01"
     },
     {
         "name": "activity-compose",
@@ -112,7 +112,7 @@
     {
         "name": "biometric",
         "group": "androidx.biometric",
-        "version": "1.4.0-alpha03"
+        "version": "1.4.0-alpha04"
     },
     {
         "name": "browser",
@@ -322,7 +322,7 @@
     {
         "name": "concurrent-futures",
         "group": "androidx.concurrent",
-        "version": "1.3.0-alpha01"
+        "version": "1.3.0-beta01"
     },
     {
         "name": "concurrent-futures-ktx",
@@ -382,7 +382,7 @@
     {
         "name": "credentials",
         "group": "androidx.credentials",
-        "version": "1.6.0-alpha01"
+        "version": "1.6.0-alpha02"
     },
     {
         "name": "credentials-play-services-auth",
@@ -717,7 +717,7 @@
     {
         "name": "mediarouter",
         "group": "androidx.mediarouter",
-        "version": "1.8.0-beta01"
+        "version": "1.8.0-rc01"
     },
     {
         "name": "multidex",
@@ -757,12 +757,12 @@
     {
         "name": "navigationevent",
         "group": "androidx.navigationevent",
-        "version": "1.0.0-SNAPSHOT"
+        "version": "1.0.0-alpha01"
     },
     {
         "name": "navigationevent-android",
         "group": "androidx.navigationevent",
-        "version": "1.0.0-SNAPSHOT"
+        "version": "1.0.0-alpha01"
     },
     {
         "name": "paging-common",
diff --git a/third_party/android_deps/autorolled/build.gradle b/third_party/android_deps/autorolled/build.gradle
index 8d90888..b256354 100644
--- a/third_party/android_deps/autorolled/build.gradle
+++ b/third_party/android_deps/autorolled/build.gradle
@@ -14,7 +14,7 @@
 // cache the resolved values of all the '+' versions when we are not trying to
 // roll (e.g. when the main project imports this subproject).
 project.ext.versionCache = [:]
-versionCache['androidx.activity:activity'] = '1.12.0-SNAPSHOT'
+versionCache['androidx.activity:activity'] = '1.12.0-alpha01'
 versionCache['androidx.activity:activity-compose'] = '1.12.0-SNAPSHOT'
 versionCache['androidx.activity:activity-ktx'] = '1.12.0-SNAPSHOT'
 versionCache['androidx.annotation:annotation'] = '1.10.0-SNAPSHOT'
@@ -36,7 +36,7 @@
 versionCache['androidx.benchmark:benchmark-macro-junit4'] = '1.4.0-SNAPSHOT'
 versionCache['androidx.benchmark:benchmark-traceprocessor'] = '1.4.0-SNAPSHOT'
 versionCache['androidx.benchmark:benchmark-traceprocessor-android'] = '1.4.0-SNAPSHOT'
-versionCache['androidx.biometric:biometric'] = '1.4.0-alpha03'
+versionCache['androidx.biometric:biometric'] = '1.4.0-alpha04'
 versionCache['androidx.browser:browser'] = '1.9.0-SNAPSHOT'
 versionCache['androidx.cardview:cardview'] = '1.1.0-SNAPSHOT'
 versionCache['androidx.collection:collection'] = '1.5.0'
@@ -78,7 +78,7 @@
 versionCache['androidx.compose.ui:ui-unit-android'] = '1.9.0-SNAPSHOT'
 versionCache['androidx.compose.ui:ui-util'] = '1.9.0-SNAPSHOT'
 versionCache['androidx.compose.ui:ui-util-android'] = '1.9.0-SNAPSHOT'
-versionCache['androidx.concurrent:concurrent-futures'] = '1.3.0-alpha01'
+versionCache['androidx.concurrent:concurrent-futures'] = '1.3.0-beta01'
 versionCache['androidx.concurrent:concurrent-futures-ktx'] = '1.3.0-SNAPSHOT'
 versionCache['androidx.constraintlayout:constraintlayout'] = '2.3.0-SNAPSHOT'
 versionCache['androidx.constraintlayout:constraintlayout-core'] = '1.2.0-SNAPSHOT'
@@ -90,7 +90,7 @@
 versionCache['androidx.core:core-viewtree'] = '1.1.0-SNAPSHOT'
 versionCache['androidx.credentials.registry:registry-provider'] = '1.0.0-SNAPSHOT'
 versionCache['androidx.credentials.registry:registry-provider-play-services'] = '1.0.0-SNAPSHOT'
-versionCache['androidx.credentials:credentials'] = '1.6.0-alpha01'
+versionCache['androidx.credentials:credentials'] = '1.6.0-alpha02'
 versionCache['androidx.credentials:credentials-play-services-auth'] = '1.6.0-SNAPSHOT'
 versionCache['androidx.cursoradapter:cursoradapter'] = '1.1.0-SNAPSHOT'
 versionCache['androidx.customview:customview'] = '1.2.0'
@@ -157,7 +157,7 @@
 versionCache['androidx.media3:media3-session'] = '1.2.0'
 versionCache['androidx.media3:media3-ui'] = '1.2.0'
 versionCache['androidx.media:media'] = '1.8.0-SNAPSHOT'
-versionCache['androidx.mediarouter:mediarouter'] = '1.8.0-beta01'
+versionCache['androidx.mediarouter:mediarouter'] = '1.8.0-rc01'
 versionCache['androidx.multidex:multidex'] = '2.0.0'
 versionCache['androidx.navigation:navigation-common'] = '2.10.0-SNAPSHOT'
 versionCache['androidx.navigation:navigation-common-android'] = '2.10.0-SNAPSHOT'
@@ -165,8 +165,8 @@
 versionCache['androidx.navigation:navigation-compose-android'] = '2.10.0-SNAPSHOT'
 versionCache['androidx.navigation:navigation-runtime'] = '2.10.0-SNAPSHOT'
 versionCache['androidx.navigation:navigation-runtime-android'] = '2.10.0-SNAPSHOT'
-versionCache['androidx.navigationevent:navigationevent'] = '1.0.0-SNAPSHOT'
-versionCache['androidx.navigationevent:navigationevent-android'] = '1.0.0-SNAPSHOT'
+versionCache['androidx.navigationevent:navigationevent'] = '1.0.0-alpha01'
+versionCache['androidx.navigationevent:navigationevent-android'] = '1.0.0-alpha01'
 versionCache['androidx.paging:paging-common'] = '3.4.0-SNAPSHOT'
 versionCache['androidx.paging:paging-common-android'] = '3.4.0-SNAPSHOT'
 versionCache['androidx.paging:paging-common-ktx'] = '3.4.0-SNAPSHOT'
diff --git a/third_party/angle b/third_party/angle
index 71b58cd..70b90f2 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 71b58cdcd7311f9572b064da0a2871e7c313535e
+Subproject commit 70b90f222c491cb9eb678e03f7a3fbf9f99932b5
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 58caf00..56fe1508 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1153,10 +1153,6 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
-BASE_FEATURE(kHTMLParserYieldByUserTiming,
-             "HTMLParserYieldByUserTiming",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE_PARAM(std::string,
                    kHTMLParserYieldEventNameForPause,
                    &kHTMLParserYieldByUserTiming,
@@ -1284,7 +1280,7 @@
 
 BASE_FEATURE(kAttributionReportingInBrowserMigration,
              "AttributionReportingInBrowserMigration",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kLimitLayerMergeDistance,
              "LimitLayerMergeDistance",
@@ -2774,7 +2770,7 @@
 #if BUILDFLAG(IS_ANDROID)
              base::FEATURE_DISABLED_BY_DEFAULT);
 #else
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_ANDROID)
 
 /// Enables cache-aware WebFonts loading. See https://crbug.com/570205.
diff --git a/third_party/blink/common/loader/throttling_url_loader.cc b/third_party/blink/common/loader/throttling_url_loader.cc
index b84306d0..7f0fed7 100644
--- a/third_party/blink/common/loader/throttling_url_loader.cc
+++ b/third_party/blink/common/loader/throttling_url_loader.cc
@@ -26,6 +26,7 @@
 #include "services/network/public/cpp/record_ontransfersizeupdate_utils.h"
 #include "services/network/public/mojom/early_hints.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
+#include "third_party/blink/public/mojom/origin_trials/origin_trial_feature.mojom-shared.h"
 
 namespace blink {
 
@@ -267,13 +268,15 @@
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     std::optional<std::vector<std::string>> cors_exempt_header_list,
-    ClientReceiverDelegate* client_receiver_delegate) {
+    ClientReceiverDelegate* client_receiver_delegate,
+    const std::vector<int>* initiator_origin_trial_features) {
   DCHECK(url_request);
   std::unique_ptr<ThrottlingURLLoader> loader(
       new ThrottlingURLLoader(std::move(throttles), client, traffic_annotation,
                               client_receiver_delegate));
   loader->Start(std::move(factory), request_id, options, url_request,
-                std::move(task_runner), std::move(cors_exempt_header_list));
+                std::move(task_runner), std::move(cors_exempt_header_list),
+                initiator_origin_trial_features);
   return loader;
 }
 
@@ -404,7 +407,8 @@
     uint32_t options,
     network::ResourceRequest* url_request,
     scoped_refptr<base::SequencedTaskRunner> task_runner,
-    std::optional<std::vector<std::string>> cors_exempt_header_list) {
+    std::optional<std::vector<std::string>> cors_exempt_header_list,
+    const std::vector<int>* initiator_origin_trial_features) {
   TRACE_EVENT_WITH_FLOW1("loading", "ThrottlingURLLoader::Start",
                          TRACE_ID_LOCAL(this),
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
@@ -464,6 +468,14 @@
     }
   }
 
+  if (initiator_origin_trial_features &&
+      base::Contains(
+          *initiator_origin_trial_features,
+          static_cast<int>(
+              mojom::OriginTrialFeature::kDeviceBoundSessionCredentials))) {
+    url_request->allows_device_bound_session_registration = true;
+  }
+
   start_info_ = std::make_unique<StartInfo>(factory, request_id, options,
                                             url_request, std::move(task_runner),
                                             std::move(cors_exempt_header_list));
diff --git a/third_party/blink/common/origin_trials/navigation_origin_trial_features.cc b/third_party/blink/common/origin_trials/navigation_origin_trial_features.cc
index 32e533b..0bf4355 100644
--- a/third_party/blink/common/origin_trials/navigation_origin_trial_features.cc
+++ b/third_party/blink/common/origin_trials/navigation_origin_trial_features.cc
@@ -21,6 +21,7 @@
       // feature, for tests.
       blink::mojom::OriginTrialFeature::kOriginTrialsSampleAPINavigation,
       blink::mojom::OriginTrialFeature::kTextFragmentIdentifiers,
+      blink::mojom::OriginTrialFeature::kDeviceBoundSessionCredentials,
   };
   return base::Contains(kEnabledForNavigation, feature);
 }
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 2d3ffda..84c7e5f3 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -688,7 +688,6 @@
     kGetUserMediaDeferredDeviceSettingsSelection);
 #endif
 
-BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kHTMLParserYieldByUserTiming);
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE_PARAM(
     std::string,
     kHTMLParserYieldEventNameForPause);
diff --git a/third_party/blink/public/common/loader/throttling_url_loader.h b/third_party/blink/public/common/loader/throttling_url_loader.h
index 6c15622c..a26a04e 100644
--- a/third_party/blink/public/common/loader/throttling_url_loader.h
+++ b/third_party/blink/public/common/loader/throttling_url_loader.h
@@ -114,7 +114,8 @@
       scoped_refptr<base::SequencedTaskRunner> task_runner,
       std::optional<std::vector<std::string>> cors_exempt_header_list =
           std::nullopt,
-      ClientReceiverDelegate* client_receiver_delegate = nullptr);
+      ClientReceiverDelegate* client_receiver_delegate = nullptr,
+      const std::vector<int>* initiator_origin_trial_features = nullptr);
 
   ThrottlingURLLoader(const ThrottlingURLLoader&) = delete;
   ThrottlingURLLoader& operator=(const ThrottlingURLLoader&) = delete;
@@ -174,7 +175,8 @@
              uint32_t options,
              network::ResourceRequest* url_request,
              scoped_refptr<base::SequencedTaskRunner> task_runner,
-             std::optional<std::vector<std::string>> cors_exempt_header_list);
+             std::optional<std::vector<std::string>> cors_exempt_header_list,
+             const std::vector<int>* initiator_origin_trial_features);
 
   void StartNow();
   void RestartWithURLResetNow();
diff --git a/third_party/blink/renderer/bindings/IDLUnionTypes.md b/third_party/blink/renderer/bindings/IDLUnionTypes.md
index 8d42df5..eefd772 100644
--- a/third_party/blink/renderer/bindings/IDLUnionTypes.md
+++ b/third_party/blink/renderer/bindings/IDLUnionTypes.md
@@ -22,6 +22,8 @@
 The file name for a generated class is basically the same as its class
 name, but we use some aliases to avoid too-long file names
 (See https://crbug.com/611437 why we need to avoid long file names).
+Aliasing is done by adding to
+[union_name_map.conf](https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/bindings/union_name_map.conf).
 
 The paths for generated classes depend on the places union types are
 used. If a union type is used only in IDL files under modules/, the
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index c817715..430fa02 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -2986,6 +2986,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_web_transport_error.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl2_rendering_context.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl2_rendering_context.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl2_rendering_context_webgpu.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl2_rendering_context_webgpu.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_active_info.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_active_info.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_blend_func_extended.cc",
@@ -3044,6 +3046,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_renderbuffer.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_rendering_context.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_rendering_context.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_rendering_context_webgpu.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_rendering_context_webgpu.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_sampler.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_sampler.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_webgl_shader.cc",
@@ -3278,8 +3282,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_boolean_mediatrackconstraints.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_canvasfilter_string.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_canvasfilter_string.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_canvasrenderingcontext2d_gpucanvascontext_imagebitmaprenderingcontext_webgl2renderingcontext_webglrenderingcontext.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_canvasrenderingcontext2d_gpucanvascontext_imagebitmaprenderingcontext_webgl2renderingcontext_webglrenderingcontext.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_rendering_context.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_rendering_context.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_client_messageport_serviceworker.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_client_messageport_serviceworker.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_constraindomstringparameters_string_stringsequence.cc",
@@ -3306,8 +3310,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_gpuautolayoutmode_gpupipelinelayout.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_gpubufferbinding_gpuexternaltexture_gpusampler_gputextureview.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_gpubufferbinding_gpuexternaltexture_gpusampler_gputextureview.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_gpucanvascontext_imagebitmaprenderingcontext_offscreencanvasrenderingcontext2d_webgl2renderingcontext_webglrenderingcontext.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_gpucanvascontext_imagebitmaprenderingcontext_offscreencanvasrenderingcontext2d_webgl2renderingcontext_webglrenderingcontext.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_offscreen_rendering_context.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_offscreen_rendering_context.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_gpuextent3ddict_unsignedlongenforcerangesequence.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_gpuextent3ddict_unsignedlongenforcerangesequence.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_gpuorigin2ddict_unsignedlongenforcerangesequence.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index e378f59..e48d158c 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -997,6 +997,7 @@
   "//third_party/blink/renderer/modules/webgl/ovr_multiview_2.idl",
   "//third_party/blink/renderer/modules/webgl/webgl2_rendering_context.idl",
   "//third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.idl",
+  "//third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.idl",
   "//third_party/blink/renderer/modules/webgl/webgl_active_info.idl",
   "//third_party/blink/renderer/modules/webgl/webgl_blend_func_extended.idl",
   "//third_party/blink/renderer/modules/webgl/webgl_buffer.idl",
@@ -1029,6 +1030,7 @@
   "//third_party/blink/renderer/modules/webgl/webgl_renderbuffer.idl",
   "//third_party/blink/renderer/modules/webgl/webgl_rendering_context.idl",
   "//third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl",
+  "//third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.idl",
   "//third_party/blink/renderer/modules/webgl/webgl_sampler.idl",
   "//third_party/blink/renderer/modules/webgl/webgl_shader.idl",
   "//third_party/blink/renderer/modules/webgl/webgl_shader_pixel_local_storage.idl",
diff --git a/third_party/blink/renderer/bindings/union_name_map.conf b/third_party/blink/renderer/bindings/union_name_map.conf
index f7036a9..ec4fa86 100644
--- a/third_party/blink/renderer/bindings/union_name_map.conf
+++ b/third_party/blink/renderer/bindings/union_name_map.conf
@@ -22,3 +22,5 @@
 
 MapUnionName("LanguageModelPromptContent")
 MapUnionName("LanguageModelPromptInput")
+MapUnionName("RenderingContext")
+MapUnionName("OffscreenRenderingContext")
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 1c5e2fff..7adedf8 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -6631,7 +6631,8 @@
     {
       name: "row-rule-width",
       property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"],
-      interpolable: true,
+      // TODO(crbug.com/357648037): Turn this on when implementing interpolation for Gap Decorations.
+      interpolable: false,
       field_group: "*",
       field_template: "external",
       default_value: "GapDataList<int>::DefaultGapWidthDataList()",
diff --git a/third_party/blink/renderer/core/css/properties/css_property_test.cc b/third_party/blink/renderer/core/css/properties/css_property_test.cc
index 876d46c..63c03e8 100644
--- a/third_party/blink/renderer/core/css/properties/css_property_test.cc
+++ b/third_party/blink/renderer/core/css/properties/css_property_test.cc
@@ -151,7 +151,16 @@
       CSSProperty::Get(CSSPropertyID::kInternalFontSizeDelta).IsWebExposed());
 }
 
-TEST_F(CSSPropertyTest, VisitedPropertiesCanParseValues) {
+class VisitedPropertiesCanParseValues
+    : public CSSPropertyTest,
+      public testing::WithParamInterface<bool> {};
+
+INSTANTIATE_TEST_SUITE_P(CSSPropertyTest,
+                         VisitedPropertiesCanParseValues,
+                         ::testing::Bool());
+
+TEST_P(VisitedPropertiesCanParseValues, ParsesAllProperties) {
+  ScopedCSSGapDecorationForTest scoped_gap_decoration(GetParam());
   const ComputedStyle& initial_style =
       GetDocument().GetStyleResolver().InitialStyle();
 
@@ -178,9 +187,30 @@
     const CSSValue* parsed_visited_value = css_test_helpers::ParseLonghand(
         GetDocument(), *visited, initial_value->CssText());
 
-    // The properties should have identical parsing behavior.
-    EXPECT_TRUE(
-        base::ValuesEquivalent(parsed_regular_value, parsed_visited_value));
+    // Special handling for 'column-rule-color' when gap decorations are
+    // enabled. In this case, the regular property returns a `CSSValueList` with
+    // a single value, while the visited property returns a single `CSSValue`.
+    // This discrepancy arises because visited styles are not applied to
+    // 'column-rule-color' when multiple values are used. To ensure accurate
+    // comparison, we extract the sole value from the regular property's list
+    // and compare it directly with the visited value.
+    //
+    // TODO(crbug.com/357648037): Remove this check once the visited
+    // partitioning work is done.
+    if (GetParam() && property_id == CSSPropertyID::kColumnRuleColor) {
+      EXPECT_TRUE(parsed_regular_value->IsValueList());
+      const CSSValueList* parsed_regular_value_list =
+          DynamicTo<CSSValueList>(parsed_regular_value);
+      EXPECT_EQ(parsed_regular_value_list->length(), 1);
+      const CSSValue* parsed_regular_color =
+          &parsed_regular_value_list->Item(0);
+      EXPECT_TRUE(
+          base::ValuesEquivalent(parsed_regular_color, parsed_visited_value));
+    } else {
+      // The properties should have identical parsing behavior.
+      EXPECT_TRUE(
+          base::ValuesEquivalent(parsed_regular_value, parsed_visited_value));
+    }
 
     num_visited++;
   }
diff --git a/third_party/blink/renderer/core/dom/quota_exceeded_error.cc b/third_party/blink/renderer/core/dom/quota_exceeded_error.cc
index 24c6250..cde7164 100644
--- a/third_party/blink/renderer/core/dom/quota_exceeded_error.cc
+++ b/third_party/blink/renderer/core/dom/quota_exceeded_error.cc
@@ -56,6 +56,22 @@
   }
 }
 
+// static
+void QuotaExceededError::Reject(ScriptPromiseResolverBase* resolver,
+                                const String& message,
+                                std::optional<double> quota,
+                                std::optional<double> requested) {
+  if (RuntimeEnabledFeatures::QuotaExceededErrorUpdateEnabled()) {
+    ScriptState* script_state = resolver->GetScriptState();
+    ScriptState::Scope scope(script_state);
+    v8::Isolate* isolate = script_state->GetIsolate();
+    resolver->Reject(Create(isolate, message, quota, requested));
+  } else {
+    resolver->RejectWithDOMException(DOMExceptionCode::kQuotaExceededError,
+                                     message);
+  }
+}
+
 QuotaExceededError::QuotaExceededError(const String& message,
                                        const QuotaExceededErrorOptions* options)
     : DOMException(DOMExceptionCode::kQuotaExceededError, message),
diff --git a/third_party/blink/renderer/core/dom/quota_exceeded_error.h b/third_party/blink/renderer/core/dom/quota_exceeded_error.h
index 2cac146..79db328b 100644
--- a/third_party/blink/renderer/core/dom/quota_exceeded_error.h
+++ b/third_party/blink/renderer/core/dom/quota_exceeded_error.h
@@ -7,6 +7,7 @@
 
 #include <optional>
 
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -37,6 +38,12 @@
                     std::optional<double> quota = std::nullopt,
                     std::optional<double> requested = std::nullopt);
 
+  // For throwing a QuotaExceededError from ScriptPromiseResolverBase::Reject.
+  static void Reject(ScriptPromiseResolverBase* resolver,
+                     const String& message,
+                     std::optional<double> quota = std::nullopt,
+                     std::optional<double> requested = std::nullopt);
+
   QuotaExceededError(const String& message,
                      const QuotaExceededErrorOptions* options);
   QuotaExceededError(const String& message,
diff --git a/third_party/blink/renderer/core/fetch/request.idl b/third_party/blink/renderer/core/fetch/request.idl
index 2212c1a..c1dcdc968 100644
--- a/third_party/blink/renderer/core/fetch/request.idl
+++ b/third_party/blink/renderer/core/fetch/request.idl
@@ -70,7 +70,7 @@
     readonly attribute boolean keepalive;
     readonly attribute AbortSignal signal;
     readonly attribute RequestDuplex duplex;
-    [RuntimeEnabled=PrivateNetworkAccessPermissionPrompt] readonly attribute IPAddressSpace targetAddressSpace;
+    [RuntimeEnabled=LocalNetworkAccessPermissionPolicy] readonly attribute IPAddressSpace targetAddressSpace;
 
     [MeasureAs=RequestIsHistoryNavigation] readonly attribute boolean isHistoryNavigation;
     [RaisesException, CallWith=ScriptState, NewObject] Request clone();
diff --git a/third_party/blink/renderer/core/fetch/request_init.idl b/third_party/blink/renderer/core/fetch/request_init.idl
index 634b2d1..5f72927 100644
--- a/third_party/blink/renderer/core/fetch/request_init.idl
+++ b/third_party/blink/renderer/core/fetch/request_init.idl
@@ -27,7 +27,7 @@
     [RuntimeEnabled=SharedStorageAPI, Exposed=Window] boolean sharedStorageWritable;
     AbortSignal? signal;
     [RuntimeEnabled=FetchUploadStreaming] RequestDuplex duplex;
-    [RuntimeEnabled=PrivateNetworkAccessPermissionPrompt] IPAddressSpace targetAddressSpace;
+    [RuntimeEnabled=LocalNetworkAccessPermissionPolicy] IPAddressSpace targetAddressSpace;
     // Even though Private Token and Attribution Reporting operations are only
     // available in secure contexts, this has to be enforced after the fact
     // because the SecureContext IDL attribute doesn't affect dictionary members.
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
index 392b7a1c..9987daaa 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
@@ -79,10 +79,8 @@
 class ImageBitmap;
 class ScriptState;
 class StaticBitmapImage;
-using V8RenderingContext = class
-    V8UnionCanvasRenderingContext2DOrGPUCanvasContextOrImageBitmapRenderingContextOrWebGL2RenderingContextOrWebGLRenderingContext;
-using V8OffscreenRenderingContext = class
-    V8UnionGPUCanvasContextOrImageBitmapRenderingContextOrOffscreenCanvasRenderingContext2DOrWebGL2RenderingContextOrWebGLRenderingContext;
+class V8RenderingContext;
+class V8OffscreenRenderingContext;
 class WebGraphicsContext3DVideoFramePool;
 
 class CORE_EXPORT CanvasRenderingContext
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser_loading_test.cc b/third_party/blink/renderer/core/html/parser/html_document_parser_loading_test.cc
index 56be35c..c2c76f0b 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser_loading_test.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser_loading_test.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/html/parser/html_document_parser.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
@@ -422,6 +423,8 @@
 class HTMLDocumentParserYieldByUserTimingTest : public SimTest {
  public:
   HTMLDocumentParserYieldByUserTimingTest() {
+    WebRuntimeFeatures::EnableFeatureFromString("HTMLParserYieldByUserTiming",
+                                                /*enable=*/true);
     std::map<std::string, std::string> params;
     params["pause_event_name"] = "pause";
     params["resume_event_name"] = "resume";
@@ -488,7 +491,7 @@
   // Flush tasks on the task queue. The resume event is scheduled with the
   // timeout and contents after the script will be available after the resume
   // event.
-  platform_->test_task_runner()->FastForwardUntilNoTasksRemain();
+  platform_->test_task_runner()->FastForwardBy(base::Milliseconds(30));
   EXPECT_TRUE(GetDocument().getElementById(AtomicString("after")));
 }
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 094f7359..b021464b 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -3766,14 +3766,17 @@
 scoped_refptr<BackgroundCodeCacheHost>
 DocumentLoader::CreateBackgroundCodeCacheHost() {
   if (!pending_code_cache_host_for_background_) {
-    return nullptr;
+    // If the Document was loaded without a navigation,
+    // `pending_code_cache_host_for_background_` is not set. In that case, get
+    // the CodeCacheHost mojo handle from the frame's BrowserInterfaceBroker
+    return base::MakeRefCounted<BackgroundCodeCacheHost>(CreateCodeCacheHost());
   }
   return base::MakeRefCounted<BackgroundCodeCacheHost>(
       std::move(pending_code_cache_host_for_background_));
 }
 
 mojo::PendingRemote<mojom::blink::CodeCacheHost>
-DocumentLoader::CreateWorkerCodeCacheHost() {
+DocumentLoader::CreateCodeCacheHost() {
   if (GetDisableCodeCacheForTesting())
     return mojo::NullRemote();
   mojo::PendingRemote<mojom::blink::CodeCacheHost> pending_code_cache_host;
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index 01b97e56..bcf51f743 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -425,7 +425,8 @@
   scoped_refptr<BackgroundCodeCacheHost> CreateBackgroundCodeCacheHost();
   static void DisableCodeCacheForTesting();
 
-  mojo::PendingRemote<mojom::blink::CodeCacheHost> CreateWorkerCodeCacheHost();
+  // This method is used for workers and iframes loaded without navigation.
+  mojo::PendingRemote<mojom::blink::CodeCacheHost> CreateCodeCacheHost();
 
   HashMap<KURL, EarlyHintsPreloadEntry> GetEarlyHintsPreloadedResources();
 
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index eb4422f9..18e0efa 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -1824,7 +1824,7 @@
 FrameLoader::CreateWorkerCodeCacheHost() {
   if (!document_loader_)
     return mojo::NullRemote();
-  return document_loader_->CreateWorkerCodeCacheHost();
+  return document_loader_->CreateCodeCacheHost();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/preload_helper.cc b/third_party/blink/renderer/core/loader/preload_helper.cc
index eccf165..e643416 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.cc
+++ b/third_party/blink/renderer/core/loader/preload_helper.cc
@@ -6,7 +6,10 @@
 
 #include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/rand_util.h"
 #include "base/timer/elapsed_timer.h"
+#include "base/types/cxx23_to_underlying.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
@@ -59,6 +62,7 @@
 #include "third_party/blink/renderer/platform/loader/link_header.h"
 #include "third_party/blink/renderer/platform/loader/subresource_integrity.h"
 #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
 
@@ -264,6 +268,26 @@
   }
 }
 
+PreloadHelper::OriginStatusOnSubresource GetOriginStatus(bool from_same_origin,
+                                                         bool to_same_origin) {
+  using OriginStatusOnSubresource = PreloadHelper::OriginStatusOnSubresource;
+  if (from_same_origin) {
+    if (to_same_origin) {
+      return OriginStatusOnSubresource::kFromSameOriginToSameOrigin;
+    } else {
+      return OriginStatusOnSubresource::kFromSameOriginToCrossOrigin;
+    }
+  } else {
+    if (to_same_origin) {
+      return OriginStatusOnSubresource::kFromCrossOriginToSameOrigin;
+    } else {
+      return OriginStatusOnSubresource::kFromCrossOriginToCrossOrigin;
+    }
+  }
+}
+
+constexpr double kUkmSamplingRate = 0.0025;
+
 }  // namespace
 
 void PreloadHelper::DnsPrefetchIfNeeded(
@@ -796,6 +820,13 @@
   base::UmaHistogramEnumeration("Blink.LinkHeader.LoadLinksFromHeaderMode",
                                 mode);
 
+  const bool is_subresource_load = IsSubresourceLoad(mode);
+  const bool from_same_origin =
+      document ? document->GetExecutionContext()
+                     ->GetSecurityOrigin()
+                     ->IsSameOriginWith(SecurityOrigin::Create(base_url).get())
+               : false;
+
   LinkHeaderSet header_set(header_value);
   for (auto& header : header_set) {
     if (!header.Valid() || header.Url().empty() || header.Rel().empty()) {
@@ -814,11 +845,26 @@
     LinkLoadParameters params(header, base_url);
     bool change_rel_to_prefetch = false;
 
+    // Record UKM by the rate of `kUkmSamplingRate` to avoid UKM infra's
+    // automatic downsampling.
+    if (is_subresource_load && base::RandDouble() < kUkmSamplingRate) {
+      CHECK(document);
+      bool to_same_origin =
+          document->GetExecutionContext()
+              ->GetSecurityOrigin()
+              ->IsSameOriginWith(SecurityOrigin::Create(params.href).get());
+      const OriginStatusOnSubresource origin_status =
+          GetOriginStatus(from_same_origin, to_same_origin);
+      ukm::builders::Blink_Preloading_ByLinkHeader(document->UkmSourceID())
+          .SetOriginStatusOnSubresource(base::to_underlying(origin_status))
+          .Record(document->UkmRecorder());
+    }
+
     // For security purposes, set `referrerpolicy: "no-referrer"` in link loads
     // from subresources. See https://crbug.com/415810136 for details.
     if (base::FeatureList::IsEnabled(
             blink::features::kNoReferrerForPreloadFromSubresource)) {
-      if (IsSubresourceLoad(mode)) {
+      if (is_subresource_load) {
         params.referrer_policy = network::mojom::ReferrerPolicy::kNever;
       }
     }
diff --git a/third_party/blink/renderer/core/loader/preload_helper.h b/third_party/blink/renderer/core/loader/preload_helper.h
index 24b3e269..07723d3 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.h
+++ b/third_party/blink/renderer/core/loader/preload_helper.h
@@ -40,6 +40,23 @@
   };
   // LINT.ThenChange(//tools/metrics/histograms/metadata/blink/enums.xml:LoadLinksFromHeaderMode)
 
+  // Distinguishes whether a preloading request is initiated by a resource from
+  // 'Same-' or 'Cross-' origin from the document's origin, and whether the
+  // request refers to the resource from 'Same-' or 'Cross-' origin from the
+  // documents's one as well.
+  //
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  // LINT.IfChange(OriginStatusOnSubresource)
+  enum class OriginStatusOnSubresource {
+    kFromSameOriginToSameOrigin = 0,
+    kFromSameOriginToCrossOrigin = 1,
+    kFromCrossOriginToSameOrigin = 2,
+    kFromCrossOriginToCrossOrigin = 3,
+    kMaxValue = kFromCrossOriginToCrossOrigin,
+  };
+  // LINT.ThenChange(//tools/metrics/histograms/metadata/blink/enums.xml:OriginStatusOnSubresource)
+
   static void LoadLinksFromHeader(
       const String& header_value,
       const KURL& base_url,
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index 35630fb4..367fdd8 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -898,7 +898,8 @@
       }
     }
 
-    if (base::FeatureList::IsEnabled(features::kHTMLParserYieldByUserTiming)) {
+    if (RuntimeEnabledFeatures::HTMLParserYieldByUserTimingEnabled() &&
+        !mark_parser_blocking.empty() && !mark_parser_restart.empty()) {
       DCHECK_NE(mark_parser_blocking, "");
       DCHECK_NE(mark_parser_restart, "");
       static const size_t timeout =
@@ -907,7 +908,7 @@
       if (window && window->GetFrame() &&
           window->GetFrame()->IsOutermostMainFrame()) {
         Document* document = window->GetFrame()->GetDocument();
-        if (mark_name == mark_parser_blocking && mark_parser_blocking != "") {
+        if (mark_name == mark_parser_blocking) {
           document->NotifyParserPauseByUserTiming();
           // Schedule a timeout based resume event here since pausing the parser
           // can be a potential footgun. It's not guaranteed that the parser
@@ -924,7 +925,7 @@
                   },
                   WrapPersistent(document)),
               base::Milliseconds(timeout));
-        } else if (mark_name == mark_parser_restart && mark_parser_restart != "") {
+        } else if (mark_name == mark_parser_restart) {
           // If the parser is pausing, resume it.
           document->NotifyParserResumeByUserTiming();
           parser_yield_task_handle_.Cancel();
diff --git a/third_party/blink/renderer/modules/ai/ai_writing_assistance_create_client.h b/third_party/blink/renderer/modules/ai/ai_writing_assistance_create_client.h
index aad2666..bf65c12 100644
--- a/third_party/blink/renderer/modules/ai/ai_writing_assistance_create_client.h
+++ b/third_party/blink/renderer/modules/ai/ai_writing_assistance_create_client.h
@@ -7,6 +7,7 @@
 
 #include "base/task/sequenced_task_runner.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_create_monitor_callback.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/ai/ai_context_observer.h"
@@ -137,9 +138,8 @@
         break;
       }
       case AIManagerCreateClientError::kInitialInputTooLarge: {
-        this->GetResolver()->RejectWithDOMException(
-            DOMExceptionCode::kQuotaExceededError,
-            kExceptionMessageInputTooLarge);
+        QuotaExceededError::Reject(this->GetResolver(),
+                                   kExceptionMessageInputTooLarge);
         break;
       }
       case AIManagerCreateClientError::kUnsupportedLanguage: {
diff --git a/third_party/blink/renderer/modules/ai/exception_helpers.cc b/third_party/blink/renderer/modules/ai/exception_helpers.cc
index 8385658..1cd8ed8 100644
--- a/third_party/blink/renderer/modules/ai/exception_helpers.cc
+++ b/third_party/blink/renderer/modules/ai/exception_helpers.cc
@@ -8,12 +8,15 @@
 #include "base/notreached.h"
 #include "third_party/blink/public/mojom/ai/ai_manager.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/ai/ai_manager.mojom-shared.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_quota_exceeded_error_options.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
@@ -230,6 +233,11 @@
           kExceptionMessageSessionDestroyed,
           DOMException::GetErrorName(DOMExceptionCode::kInvalidStateError));
     case ModelStreamingResponseStatus::kErrorInputTooLarge:
+      if (RuntimeEnabledFeatures::QuotaExceededErrorUpdateEnabled()) {
+        return QuotaExceededError::Create(
+            kExceptionMessageInputTooLarge,
+            MakeGarbageCollected<QuotaExceededErrorOptions>());
+      }
       return DOMException::Create(
           kExceptionMessageInputTooLarge,
           DOMException::GetErrorName(DOMExceptionCode::kQuotaExceededError));
diff --git a/third_party/blink/renderer/modules/ai/language_model_create_client.cc b/third_party/blink/renderer/modules/ai/language_model_create_client.cc
index abd68e0..cd5190e 100644
--- a/third_party/blink/renderer/modules/ai/language_model_create_client.cc
+++ b/third_party/blink/renderer/modules/ai/language_model_create_client.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_language_model_prompt_dict.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_language_model_prompt_content.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_languagemodelpromptdict_string.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/ai/ai_interface_proxy.h"
@@ -181,9 +182,7 @@
       break;
     }
     case AIManagerCreateClientError::kInitialInputTooLarge: {
-      GetResolver()->RejectWithDOMException(
-          DOMExceptionCode::kQuotaExceededError,
-          kExceptionMessageInputTooLarge);
+      QuotaExceededError::Reject(GetResolver(), kExceptionMessageInputTooLarge);
       break;
     }
     case AIManagerCreateClientError::kUnsupportedLanguage: {
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc
index fb27c15..8402afa 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_background_fetch_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_image_resource.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/fetch/body.h"
 #include "third_party/blink/renderer/core/fetch/body_stream_buffer.h"
 #include "third_party/blink/renderer/core/fetch/request.h"
@@ -295,8 +296,7 @@
           "There is no service worker available to service the fetch."));
       return;
     case mojom::blink::BackgroundFetchError::QUOTA_EXCEEDED:
-      resolver->RejectWithDOMException(DOMExceptionCode::kQuotaExceededError,
-                                       "Quota exceeded.");
+      QuotaExceededError::Reject(resolver, "Quota exceeded.");
       return;
     case mojom::blink::BackgroundFetchError::REGISTRATION_LIMIT_EXCEEDED:
       resolver->Reject(V8ThrowException::CreateTypeError(
diff --git a/third_party/blink/renderer/modules/buckets/storage_bucket_manager.cc b/third_party/blink/renderer/modules/buckets/storage_bucket_manager.cc
index 8bd7fb0..d95c3c9 100644
--- a/third_party/blink/renderer/modules/buckets/storage_bucket_manager.cc
+++ b/third_party/blink/renderer/modules/buckets/storage_bucket_manager.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_storage_bucket_options.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/execution_context/navigator_base.h"
 #include "third_party/blink/renderer/modules/buckets/storage_bucket.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -235,9 +236,7 @@
             "Unknown error occured while creating a bucket."));
         return;
       case mojom::blink::BucketError::kQuotaExceeded:
-        resolver->Reject(MakeGarbageCollected<DOMException>(
-            DOMExceptionCode::kQuotaExceededError,
-            "Too many buckets created."));
+        QuotaExceededError::Reject(resolver, "Too many buckets created.");
         return;
       case mojom::blink::BucketError::kInvalidExpiration:
         resolver->Reject(V8ThrowException::CreateTypeError(
diff --git a/third_party/blink/renderer/modules/cache_storage/cache_storage_error.cc b/third_party/blink/renderer/modules/cache_storage/cache_storage_error.cc
index e8932d27..7e73589 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache_storage_error.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache_storage_error.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/modules/cache_storage/cache.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -48,7 +49,6 @@
                                  mojom::blink::CacheStorageError web_error,
                                  const String& message) {
   String final_message =
-
       !message.empty() ? message : GetDefaultMessage(web_error);
   switch (web_error) {
     case mojom::CacheStorageError::kSuccess:
@@ -67,8 +67,7 @@
                                        final_message);
       return;
     case mojom::CacheStorageError::kErrorQuotaExceeded:
-      resolver->RejectWithDOMException(DOMExceptionCode::kQuotaExceededError,
-                                       final_message);
+      QuotaExceededError::Reject(resolver, final_message);
       return;
     case mojom::CacheStorageError::kErrorCacheNameNotFound:
       resolver->RejectWithDOMException(DOMExceptionCode::kNotFoundError,
diff --git a/third_party/blink/renderer/modules/canvas/BUILD.gn b/third_party/blink/renderer/modules/canvas/BUILD.gn
index 9b5c305..51ad545 100644
--- a/third_party/blink/renderer/modules/canvas/BUILD.gn
+++ b/third_party/blink/renderer/modules/canvas/BUILD.gn
@@ -5,17 +5,6 @@
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//third_party/blink/renderer/modules/modules.gni")
 
-blink_modules_sources("canvas_proxy_headers") {
-  sources = [
-    "htmlcanvas/v8_rendering_context.h",
-    "offscreencanvas/v8_offscreen_rendering_context.h",
-  ]
-
-  if (is_win) {
-    arflags = [ "/llvmlibempty" ]
-  }
-}
-
 blink_modules_sources("canvas") {
   sources = [
     "canvas2d/base_rendering_context_2d.cc",
@@ -72,8 +61,6 @@
     "testing/canvas_test_utils.cc",
   ]
 
-  public_deps =
-      [ "//third_party/blink/renderer/modules/canvas:canvas_proxy_headers" ]
   deps = [
     "//third_party/blink/renderer/modules/webcodecs",
     "//third_party/blink/renderer/modules/webgpu",
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index 1210e29e..1c63bff 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -58,6 +58,7 @@
 #include "third_party/blink/public/mojom/scroll/scroll_enums.mojom-blink.h"
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_rendering_context.h"
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
@@ -88,7 +89,6 @@
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/path_2d.h"
 #include "third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_helpers.h"
-#include "third_party/blink/renderer/modules/canvas/htmlcanvas/v8_rendering_context.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
 #include "third_party/blink/renderer/platform/geometry/path.h"
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.idl b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.idl
index 70d0c26..261e9ad 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.idl
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.idl
@@ -7,6 +7,8 @@
 typedef (CanvasRenderingContext2D or
          WebGLRenderingContext or
          WebGL2RenderingContext or
+         WebGLRenderingContextWebGPU or
+         WebGL2RenderingContextWebGPU or
          ImageBitmapRenderingContext or
          GPUCanvasContext) RenderingContext;
 
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/v8_rendering_context.h b/third_party/blink/renderer/modules/canvas/htmlcanvas/v8_rendering_context.h
deleted file mode 100644
index cfa8ac67..0000000
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/v8_rendering_context.h
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_HTMLCANVAS_V8_RENDERING_CONTEXT_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_HTMLCANVAS_V8_RENDERING_CONTEXT_H_
-
-// This is just a forwarding header to avoid including an enormous filename in
-// each file that needs the declaration of the union that is used for
-// RenderingContext.
-#include "third_party/blink/renderer/bindings/modules/v8/v8_union_canvasrenderingcontext2d_gpucanvascontext_imagebitmaprenderingcontext_webgl2renderingcontext_webglrenderingcontext.h"
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_HTMLCANVAS_V8_RENDERING_CONTEXT_H_
diff --git a/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context.cc b/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context.cc
index 2dceeed..3179a47 100644
--- a/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context.cc
+++ b/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context.cc
@@ -6,9 +6,9 @@
 
 #include <utility>
 
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_offscreen_rendering_context.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_rendering_context.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
-#include "third_party/blink/renderer/modules/canvas/htmlcanvas/v8_rendering_context.h"
-#include "third_party/blink/renderer/modules/canvas/offscreencanvas/v8_offscreen_rendering_context.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_module.idl b/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_module.idl
index f88b06c..a15d8d67 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_module.idl
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_module.idl
@@ -7,6 +7,8 @@
 typedef (OffscreenCanvasRenderingContext2D or
          WebGLRenderingContext or
          WebGL2RenderingContext or
+         WebGLRenderingContextWebGPU or
+         WebGL2RenderingContextWebGPU or
          ImageBitmapRenderingContext or
          GPUCanvasContext) OffscreenRenderingContext;
 enum OffscreenRenderingContextType { "2d", "webgl", "webgl2", "bitmaprenderer", "webgpu" };
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas/v8_offscreen_rendering_context.h b/third_party/blink/renderer/modules/canvas/offscreencanvas/v8_offscreen_rendering_context.h
deleted file mode 100644
index b44bacfe..0000000
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas/v8_offscreen_rendering_context.h
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_OFFSCREENCANVAS_V8_OFFSCREEN_RENDERING_CONTEXT_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_OFFSCREENCANVAS_V8_OFFSCREEN_RENDERING_CONTEXT_H_
-
-// This is just a forwarding header to avoid including an enormous filename in
-// each file that needs the declaration of the union that is used for
-// OffscreenRenderingContext.
-#include "third_party/blink/renderer/bindings/modules/v8/v8_union_gpucanvascontext_imagebitmaprenderingcontext_offscreencanvasrenderingcontext2d_webgl2renderingcontext_webglrenderingcontext.h"
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_OFFSCREENCANVAS_V8_OFFSCREEN_RENDERING_CONTEXT_H_
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
index 9dfae0e..04d4fae 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_canvas_font_stretch.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_canvas_text_rendering.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_offscreen_rendering_context.h"
 #include "third_party/blink/renderer/core/css/offscreen_font_selector.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/css/resolver/font_style_resolver.h"
@@ -22,7 +23,6 @@
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worker_settings.h"
 #include "third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_helpers.h"
-#include "third_party/blink/renderer/modules/canvas/offscreencanvas/v8_offscreen_rendering_context.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/fonts/text_run_paint_info.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
diff --git a/third_party/blink/renderer/modules/crypto/crypto.cc b/third_party/blink/renderer/modules/crypto/crypto.cc
index 53ff158..2e20932 100644
--- a/third_party/blink/renderer/modules/crypto/crypto.cc
+++ b/third_party/blink/renderer/modules/crypto/crypto.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/modules/crypto/crypto.h"
 
 #include "crypto/random.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/wtf/uuid.h"
@@ -65,8 +66,8 @@
     return NotShared<DOMArrayBufferView>(nullptr);
   }
   if (array->byteLength() > 65536) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kQuotaExceededError,
+    QuotaExceededError::Throw(
+        exception_state,
         String::Format("The ArrayBufferView's byte length (%zu) exceeds the "
                        "number of bytes of entropy available via this API "
                        "(65536).",
diff --git a/third_party/blink/renderer/modules/encryptedmedia/content_decryption_module_result_promise.cc b/third_party/blink/renderer/modules/encryptedmedia/content_decryption_module_result_promise.cc
index 6b8e5134..18e9d8f 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/content_decryption_module_result_promise.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/content_decryption_module_result_promise.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -39,8 +40,7 @@
                                        message);
       return;
     case kWebContentDecryptionModuleExceptionQuotaExceededError:
-      resolver->RejectWithDOMException(DOMExceptionCode::kQuotaExceededError,
-                                       message);
+      QuotaExceededError::Reject(resolver, message);
       return;
   }
 
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
index 3db862d..95f4890 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
@@ -7,6 +7,7 @@
 #include "base/files/file_error_or.h"
 #include "base/numerics/checked_math.h"
 #include "base/types/expected_macros.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 
@@ -119,9 +120,8 @@
   RETURN_IF_ERROR(
       file_delegate()->SetLength(size), [&](base::File::Error error) {
         if (error == base::File::FILE_ERROR_NO_SPACE) {
-          exception_state.ThrowDOMException(
-              DOMExceptionCode::kQuotaExceededError,
-              "No space available for this operation");
+          QuotaExceededError::Throw(exception_state,
+                                    "No space available for this operation");
         } else if (error != base::File::FILE_OK) {
           exception_state.ThrowDOMException(
               DOMExceptionCode::kInvalidStateError, "truncate failed");
@@ -196,9 +196,8 @@
   int64_t write_end_offset;
   if (!base::CheckAdd(file_offset, write_size)
            .AssignIfValid(&write_end_offset)) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kQuotaExceededError,
-        "No capacity available for this operation");
+    QuotaExceededError::Throw(exception_state,
+                              "No capacity available for this operation");
     return 0;
   }
   DCHECK_GE(write_end_offset, 0);
@@ -208,9 +207,8 @@
       [&](base::File::Error error) {
         DCHECK_NE(error, base::File::FILE_OK);
         if (error == base::File::FILE_ERROR_NO_SPACE) {
-          exception_state.ThrowDOMException(
-              DOMExceptionCode::kQuotaExceededError,
-              "No space available for this operation");
+          QuotaExceededError::Throw(exception_state,
+                                    "No space available for this operation");
         } else {
           exception_state.ThrowDOMException(
               DOMExceptionCode::kInvalidStateError,
diff --git a/third_party/blink/renderer/modules/mediasource/media_source.cc b/third_party/blink/renderer/modules/mediasource/media_source.cc
index ae1fb585..d34f81fa 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source.cc
+++ b/third_party/blink/renderer/modules/mediasource/media_source.cc
@@ -184,6 +184,12 @@
   exception_state.ThrowDOMException(error, message);
 }
 
+void MediaSource::LogAndThrowQuotaExceededError(ExceptionState& exception_state,
+                                                const String& message) {
+  DVLOG(1) << __func__ << " (message=" << message << ")";
+  QuotaExceededError::Throw(exception_state, message);
+}
+
 void MediaSource::LogAndThrowTypeError(ExceptionState& exception_state,
                                        const String& message) {
   DVLOG(1) << __func__ << " (message=" << message << ")";
@@ -1676,11 +1682,11 @@
       // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
       // Step 3: If the user agent can't handle any more SourceBuffer objects
       // then throw a QuotaExceededError exception and abort these steps.
-      LogAndThrowDOMException(exception_state,
-                              DOMExceptionCode::kQuotaExceededError,
-                              "This MediaSource has reached the limit of "
-                              "SourceBuffer objects it can handle. No "
-                              "additional SourceBuffer objects may be added.");
+      LogAndThrowQuotaExceededError(
+          exception_state,
+          "This MediaSource has reached the limit of "
+          "SourceBuffer objects it can handle. No "
+          "additional SourceBuffer objects may be added.");
       return nullptr;
   }
 
diff --git a/third_party/blink/renderer/modules/mediasource/media_source.h b/third_party/blink/renderer/modules/mediasource/media_source.h
index 577f65f..1547ad0 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source.h
+++ b/third_party/blink/renderer/modules/mediasource/media_source.h
@@ -67,6 +67,8 @@
   static void LogAndThrowDOMException(ExceptionState&,
                                       DOMExceptionCode error,
                                       const String& message);
+  static void LogAndThrowQuotaExceededError(ExceptionState&,
+                                            const String& message);
   static void LogAndThrowTypeError(ExceptionState&, const String&);
 
   // Web-exposed methods from media_source.idl
diff --git a/third_party/blink/renderer/modules/mediasource/source_buffer.cc b/third_party/blink/renderer/modules/mediasource/source_buffer.cc
index baec20e..44abe79 100644
--- a/third_party/blink/renderer/modules/mediasource/source_buffer.cc
+++ b/third_party/blink/renderer/modules/mediasource/source_buffer.cc
@@ -1893,8 +1893,8 @@
     //    If the incoming data exceeds wtf_size_t::max, then our implementation
     //    cannot deal with it, so we also throw a QuotaExceededError.
     DVLOG(3) << __func__ << " this=" << this << " -> throw QuotaExceededError";
-    MediaSource::LogAndThrowDOMException(
-        exception_state, DOMExceptionCode::kQuotaExceededError,
+    MediaSource::LogAndThrowQuotaExceededError(
+        exception_state,
         "The SourceBuffer is full, and cannot free space to append additional "
         "buffers.");
     TRACE_EVENT_NESTABLE_ASYNC_END0("media", "SourceBuffer::prepareAppend",
@@ -1990,8 +1990,8 @@
   // just a single async segment parser loop run later, with nothing added to
   // the parser's input buffer here synchronously.
   if (!web_source_buffer_->AppendToParseBuffer(data)) {
-    MediaSource::LogAndThrowDOMException(
-        *exception_state, DOMExceptionCode::kQuotaExceededError,
+    MediaSource::LogAndThrowQuotaExceededError(
+        *exception_state,
         "Unable to allocate space required to buffer appended media.");
     TRACE_EVENT_NESTABLE_ASYNC_END0("media", "SourceBuffer::prepareAsyncAppend",
                                     TRACE_ID_LOCAL(this));
diff --git a/third_party/blink/renderer/modules/serial/serial_port.cc b/third_party/blink/renderer/modules/serial/serial_port.cc
index 538fb6c..9b2815a 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.cc
+++ b/third_party/blink/renderer/modules/serial/serial_port.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_serial_output_signals.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_serial_port_info.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
@@ -208,8 +209,7 @@
   mojo::ScopedDataPipeProducerHandle producer;
   mojo::ScopedDataPipeConsumerHandle consumer;
   if (!CreateDataPipe(&producer, &consumer)) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kQuotaExceededError,
-                                      kResourcesExhaustedReadBuffer);
+    QuotaExceededError::Throw(exception_state, kResourcesExhaustedReadBuffer);
     return nullptr;
   }
 
@@ -234,8 +234,7 @@
   mojo::ScopedDataPipeProducerHandle producer;
   mojo::ScopedDataPipeConsumerHandle consumer;
   if (!CreateDataPipe(&producer, &consumer)) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kQuotaExceededError,
-                                      kResourcesExhaustedWriteBuffer);
+    QuotaExceededError::Throw(exception_state, kResourcesExhaustedWriteBuffer);
     return nullptr;
   }
 
diff --git a/third_party/blink/renderer/modules/storage/storage_area.cc b/third_party/blink/renderer/modules/storage/storage_area.cc
index fb64596..c148b2a1 100644
--- a/third_party/blink/renderer/modules/storage/storage_area.cc
+++ b/third_party/blink/renderer/modules/storage/storage_area.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/storage/dom_window_storage.h"
@@ -128,9 +129,8 @@
     return NamedPropertySetterResult::kIntercepted;
   }
   if (!cached_area_->SetItem(key, value, this)) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kQuotaExceededError,
-        "Setting the value of '" + key + "' exceeded the quota.");
+    QuotaExceededError::Throw(exception_state, "Setting the value of '" + key +
+                                                   "' exceeded the quota.");
     return NamedPropertySetterResult::kIntercepted;
   }
   return NamedPropertySetterResult::kIntercepted;
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_logger.h b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
index 94c0823..928b6bee 100644
--- a/third_party/blink/renderer/modules/webcodecs/codec_logger.h
+++ b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
@@ -16,11 +16,13 @@
 #include "media/base/media_util.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/inspector_media_context_impl.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/wtf.h"
 
 namespace base {
@@ -182,21 +184,28 @@
       status_code_ = status.code();
     }
 
+    String sanitized_message;
+    String unsanitized_message;
     if (status.message().empty()) {
-      return MakeGarbageCollected<DOMException>(code, error_msg.c_str());
+      sanitized_message = error_msg.c_str();
+    } else {
+      // If the message comes from a bundled codec we can log it to JS.
+      sanitized_message = String::Format("%s (%s)", error_msg.c_str(),
+                                         status.message().c_str());
+      if (!can_log_status_message) {
+        // If not we can only set it as the unsanitized message which the
+        // console will show in some circumstances.
+        unsanitized_message = sanitized_message;
+        sanitized_message = error_msg.c_str();
+      }
     }
 
-    // If the message comes from a bundled codec we can log it to JS.
-    auto detailed_message =
-        String::Format("%s (%s)", error_msg.c_str(), status.message().c_str());
-    if (can_log_status_message) {
-      return MakeGarbageCollected<DOMException>(code, detailed_message);
+    if (code == DOMExceptionCode::kQuotaExceededError &&
+        RuntimeEnabledFeatures::QuotaExceededErrorUpdateEnabled()) {
+      return MakeGarbageCollected<QuotaExceededError>(sanitized_message);
     }
-
-    // If not we can only set it as the unsanitized message which the console
-    // will show in some circumstances.
-    return MakeGarbageCollected<DOMException>(code, error_msg.c_str(),
-                                              detailed_message);
+    return MakeGarbageCollected<DOMException>(code, sanitized_message,
+                                              unsanitized_message);
   }
 
   std::optional<typename StatusImpl::Codes> status_code_;
diff --git a/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.cc b/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.cc
index 0bec9ba..79f3523a 100644
--- a/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.cc
+++ b/third_party/blink/renderer/modules/webcodecs/reclaimable_codec.cc
@@ -7,10 +7,12 @@
 #include "base/location.h"
 #include "base/time/default_tick_clock.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/dom/quota_exceeded_error.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/webcodecs/codec_pressure_manager.h"
 #include "third_party/blink/renderer/modules/webcodecs/codec_pressure_manager_provider.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
@@ -127,8 +129,13 @@
 }
 
 void ReclaimableCodec::SimulateCodecReclaimedForTesting() {
-  OnCodecReclaimed(MakeGarbageCollected<DOMException>(
-      DOMExceptionCode::kQuotaExceededError, "Codec reclaimed for testing."));
+  auto* message = "Codec reclaimed for testing.";
+  if (RuntimeEnabledFeatures::QuotaExceededErrorUpdateEnabled()) {
+    OnCodecReclaimed(MakeGarbageCollected<QuotaExceededError>(message));
+  } else {
+    OnCodecReclaimed(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kQuotaExceededError, message));
+  }
 }
 
 void ReclaimableCodec::SimulateActivityTimerFiredForTesting() {
diff --git a/third_party/blink/renderer/modules/webgl/BUILD.gn b/third_party/blink/renderer/modules/webgl/BUILD.gn
index 08d7b3f..81d08a3 100644
--- a/third_party/blink/renderer/modules/webgl/BUILD.gn
+++ b/third_party/blink/renderer/modules/webgl/BUILD.gn
@@ -79,6 +79,8 @@
     "webgl2_rendering_context.h",
     "webgl2_rendering_context_base.cc",
     "webgl2_rendering_context_base.h",
+    "webgl2_rendering_context_webgpu.cc",
+    "webgl2_rendering_context_webgpu.h",
     "webgl_active_info.h",
     "webgl_blend_func_extended.cc",
     "webgl_blend_func_extended.h",
@@ -151,6 +153,10 @@
     "webgl_rendering_context.h",
     "webgl_rendering_context_base.cc",
     "webgl_rendering_context_base.h",
+    "webgl_rendering_context_webgpu.cc",
+    "webgl_rendering_context_webgpu.h",
+    "webgl_rendering_context_webgpu_base.cc",
+    "webgl_rendering_context_webgpu_base.h",
     "webgl_sampler.cc",
     "webgl_sampler.h",
     "webgl_shader.cc",
@@ -189,7 +195,6 @@
   public_deps = [ "//device/vr/public/mojom:vr_service_blink" ]
   deps = [
     "//device/vr/buildflags:buildflags",
-    "//third_party/blink/renderer/modules/canvas:canvas_proxy_headers",
     "//third_party/blink/renderer/modules/webcodecs:webcodecs",
     "//third_party/blink/renderer/modules/xr:xr",
   ]
diff --git a/third_party/blink/renderer/modules/webgl/DEPS b/third_party/blink/renderer/modules/webgl/DEPS
index ec20717..b0efa5d 100644
--- a/third_party/blink/renderer/modules/webgl/DEPS
+++ b/third_party/blink/renderer/modules/webgl/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+    "+base/notimplemented.h",
     "+device/vr/buildflags/buildflags.h",
     "+device/vr/public/mojom/vr_service.mojom-blink.h",
     "+device/vr/public/mojom/vr_service.mojom-blink-forward.h",
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context.cc
index 0774da8e..2566be0 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context.cc
@@ -5,8 +5,8 @@
 #include "third_party/blink/renderer/modules/webgl/webgl2_rendering_context.h"
 
 #include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/renderer/modules/canvas/htmlcanvas/v8_rendering_context.h"
-#include "third_party/blink/renderer/modules/canvas/offscreencanvas/v8_offscreen_rendering_context.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_offscreen_rendering_context.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_rendering_context.h"
 #include "third_party/blink/renderer/modules/webgl/ext_clip_control.h"
 #include "third_party/blink/renderer/modules/webgl/ext_color_buffer_float.h"
 #include "third_party/blink/renderer/modules/webgl/ext_color_buffer_half_float.h"
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.cc
new file mode 100644
index 0000000..d5f3bea
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.cc
@@ -0,0 +1,29 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.h"
+
+#include "base/notimplemented.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_offscreen_rendering_context.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_rendering_context.h"
+
+namespace blink {
+
+WebGL2RenderingContextWebGPU::WebGL2RenderingContextWebGPU(
+    CanvasRenderingContextHost* host,
+    const CanvasContextCreationAttributesCore& requested_attributes)
+    : WebGLRenderingContextWebGPUBase(host,
+                                      requested_attributes,
+                                      CanvasRenderingAPI::kWebgl2) {}
+
+V8RenderingContext* WebGL2RenderingContextWebGPU::AsV8RenderingContext() {
+  return MakeGarbageCollected<V8RenderingContext>(this);
+}
+
+V8OffscreenRenderingContext*
+WebGL2RenderingContextWebGPU::AsV8OffscreenRenderingContext() {
+  return MakeGarbageCollected<V8OffscreenRenderingContext>(this);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.h b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.h
new file mode 100644
index 0000000..db83b4a
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.h
@@ -0,0 +1,28 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL2_RENDERING_CONTEXT_WEBGPU_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL2_RENDERING_CONTEXT_WEBGPU_H_
+
+#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.h"
+
+namespace blink {
+
+class WebGL2RenderingContextWebGPU final
+    : public WebGLRenderingContextWebGPUBase {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  WebGL2RenderingContextWebGPU(
+      CanvasRenderingContextHost* host,
+      const CanvasContextCreationAttributesCore& requested_attributes);
+
+  // CanvasRenderingContext implementation
+  V8RenderingContext* AsV8RenderingContext() final;
+  V8OffscreenRenderingContext* AsV8OffscreenRenderingContext() final;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL2_RENDERING_CONTEXT_WEBGPU_H_
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.idl b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.idl
new file mode 100644
index 0000000..ac76ded
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.idl
@@ -0,0 +1,13 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7
+
+[
+    ActiveScriptWrappable,
+    Exposed=(Window,Worker),
+    RuntimeEnabled=WebGLOnWebGPU
+] interface WebGL2RenderingContextWebGPU { };
+WebGL2RenderingContextWebGPU includes WebGLRenderingContextBase;
+WebGL2RenderingContextWebGPU includes WebGL2RenderingContextBase;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_factory.cc b/third_party/blink/renderer/modules/webgl/webgl_context_factory.cc
index 048feec..8a983f1 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_context_factory.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_factory.cc
@@ -4,11 +4,15 @@
 
 #include "third_party/blink/renderer/modules/webgl/webgl_context_factory.h"
 
+#include "base/notimplemented.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h"
 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
 #include "third_party/blink/renderer/modules/webgl/webgl2_rendering_context.h"
+#include "third_party/blink/renderer/modules/webgl/webgl2_rendering_context_webgpu.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_context_event.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_rendering_context.h"
+#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
@@ -28,7 +32,11 @@
 CanvasRenderingContext* WebGLContextFactory::Create(
     CanvasRenderingContextHost* host,
     const CanvasContextCreationAttributesCore& attrs) {
-  return CreateInternal(host, attrs);
+  if (RuntimeEnabledFeatures::WebGLOnWebGPUEnabled()) {
+    return CreateInternalWebGPU(host, attrs);
+  } else {
+    return CreateInternal(host, attrs);
+  }
 }
 
 CanvasRenderingContext* WebGLContextFactory::CreateInternal(
@@ -98,6 +106,16 @@
   }
 }
 
+CanvasRenderingContext* WebGLContextFactory::CreateInternalWebGPU(
+    CanvasRenderingContextHost* host,
+    const CanvasContextCreationAttributesCore& attrs) {
+  if (is_webgl2_) {
+    return MakeGarbageCollected<WebGL2RenderingContextWebGPU>(host, attrs);
+  } else {
+    return MakeGarbageCollected<WebGLRenderingContextWebGPU>(host, attrs);
+  }
+}
+
 CanvasRenderingContext::CanvasRenderingAPI
 WebGLContextFactory::GetRenderingAPI() const {
   if (is_webgl2_) {
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_factory.h b/third_party/blink/renderer/modules/webgl/webgl_context_factory.h
index df24c70..e58bbe3 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_context_factory.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_factory.h
@@ -39,6 +39,9 @@
   CanvasRenderingContext* CreateInternal(
       CanvasRenderingContextHost*,
       const CanvasContextCreationAttributesCore&);
+  CanvasRenderingContext* CreateInternalWebGPU(
+      CanvasRenderingContextHost*,
+      const CanvasContextCreationAttributesCore&);
 
   const char* GetContextName() const;
   Platform::ContextType GetContextType() const;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc
index 700baae6..e8983e2 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc
@@ -28,8 +28,8 @@
 #include <memory>
 
 #include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/renderer/modules/canvas/htmlcanvas/v8_rendering_context.h"
-#include "third_party/blink/renderer/modules/canvas/offscreencanvas/v8_offscreen_rendering_context.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_offscreen_rendering_context.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_rendering_context.h"
 #include "third_party/blink/renderer/modules/webgl/angle_instanced_arrays.h"
 #include "third_party/blink/renderer/modules/webgl/ext_blend_min_max.h"
 #include "third_party/blink/renderer/modules/webgl/ext_clip_control.h"
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.cc
new file mode 100644
index 0000000..414c8b37
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.cc
@@ -0,0 +1,29 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.h"
+
+#include "base/notimplemented.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_offscreen_rendering_context.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_rendering_context.h"
+
+namespace blink {
+
+WebGLRenderingContextWebGPU::WebGLRenderingContextWebGPU(
+    CanvasRenderingContextHost* host,
+    const CanvasContextCreationAttributesCore& requested_attributes)
+    : WebGLRenderingContextWebGPUBase(host,
+                                      requested_attributes,
+                                      CanvasRenderingAPI::kWebgl) {}
+
+V8RenderingContext* WebGLRenderingContextWebGPU::AsV8RenderingContext() {
+  return MakeGarbageCollected<V8RenderingContext>(this);
+}
+
+V8OffscreenRenderingContext*
+WebGLRenderingContextWebGPU::AsV8OffscreenRenderingContext() {
+  return MakeGarbageCollected<V8OffscreenRenderingContext>(this);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.h
new file mode 100644
index 0000000..d0f3d1c3
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.h
@@ -0,0 +1,28 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_RENDERING_CONTEXT_WEBGPU_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_RENDERING_CONTEXT_WEBGPU_H_
+
+#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.h"
+
+namespace blink {
+
+class WebGLRenderingContextWebGPU final
+    : public WebGLRenderingContextWebGPUBase {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  WebGLRenderingContextWebGPU(
+      CanvasRenderingContextHost* host,
+      const CanvasContextCreationAttributesCore& requested_attributes);
+
+  // CanvasRenderingContext implementation
+  V8RenderingContext* AsV8RenderingContext() final;
+  V8OffscreenRenderingContext* AsV8OffscreenRenderingContext() final;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_RENDERING_CONTEXT_WEBGPU_H_
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.idl b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.idl
new file mode 100644
index 0000000..2f4c23c
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu.idl
@@ -0,0 +1,16 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://www.khronos.org/registry/webgl/specs/latest/1.0/#WebGLRenderingContext
+
+[
+    ActiveScriptWrappable,
+    Exposed=(Window,Worker),
+    RuntimeEnabled=WebGLOnWebGPU
+] interface WebGLRenderingContextWebGPU {
+
+    [RuntimeEnabled=WebGLDrawingBufferStorage] const GLenum RGB8   = 0x8051;
+    [RuntimeEnabled=WebGLDrawingBufferStorage] const GLenum RGBA8  = 0x8058;
+};
+WebGLRenderingContextWebGPU includes WebGLRenderingContextBase;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.cc
new file mode 100644
index 0000000..18d26cfe
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.cc
@@ -0,0 +1,2475 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.h"
+
+#include "base/notimplemented.h"
+
+namespace blink {
+
+WebGLRenderingContextWebGPUBase::WebGLRenderingContextWebGPUBase(
+    CanvasRenderingContextHost* host,
+    const CanvasContextCreationAttributesCore& requested_attributes,
+    CanvasRenderingAPI api)
+    : WebGLContextObjectSupport(
+          host->GetTopExecutionContext()->GetTaskRunner(TaskType::kWebGL),
+          /* is_webgl2 */ api == CanvasRenderingAPI::kWebgl2),
+      CanvasRenderingContext(host, requested_attributes, api) {}
+
+WebGLRenderingContextWebGPUBase::~WebGLRenderingContextWebGPUBase() {}
+
+// ****************************************************************************
+// Start of WebGLRenderingContextBase's IDL methods
+// ****************************************************************************
+
+V8UnionHTMLCanvasElementOrOffscreenCanvas*
+WebGLRenderingContextWebGPUBase::getHTMLOrOffscreenCanvas() const {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+int WebGLRenderingContextWebGPUBase::drawingBufferWidth() const {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+int WebGLRenderingContextWebGPUBase::drawingBufferHeight() const {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+GLenum WebGLRenderingContextWebGPUBase::drawingBufferFormat() const {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+V8PredefinedColorSpace
+WebGLRenderingContextWebGPUBase::drawingBufferColorSpace() const {
+  NOTIMPLEMENTED();
+  return V8PredefinedColorSpace(V8PredefinedColorSpace::Enum::kSRGB);
+}
+
+void WebGLRenderingContextWebGPUBase::setDrawingBufferColorSpace(
+    const V8PredefinedColorSpace& color_space,
+    ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+V8PredefinedColorSpace WebGLRenderingContextWebGPUBase::unpackColorSpace()
+    const {
+  NOTIMPLEMENTED();
+  return V8PredefinedColorSpace(V8PredefinedColorSpace::Enum::kSRGB);
+}
+
+void WebGLRenderingContextWebGPUBase::setUnpackColorSpace(
+    const V8PredefinedColorSpace& color_space,
+    ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::activeTexture(GLenum texture) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::attachShader(WebGLProgram*,
+                                                   WebGLShader*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bindAttribLocation(WebGLProgram*,
+                                                         GLuint index,
+                                                         const String& name) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bindBuffer(GLenum target,
+                                                 WebGLBuffer* buffer) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bindFramebuffer(GLenum target,
+                                                      WebGLFramebuffer*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bindRenderbuffer(GLenum target,
+                                                       WebGLRenderbuffer*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bindTexture(GLenum target,
+                                                  WebGLTexture*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::blendColor(GLfloat red,
+                                                 GLfloat green,
+                                                 GLfloat blue,
+                                                 GLfloat alpha) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::blendEquation(GLenum mode) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::blendEquationSeparate(GLenum mode_rgb,
+                                                            GLenum mode_alpha) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::blendFunc(GLenum sfactor,
+                                                GLenum dfactor) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::blendFuncSeparate(GLenum src_rgb,
+                                                        GLenum dst_rgb,
+                                                        GLenum src_alpha,
+                                                        GLenum dst_alpha) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bufferData(GLenum target,
+                                                 int64_t size,
+                                                 GLenum usage) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bufferData(GLenum target,
+                                                 DOMArrayBufferBase* data,
+                                                 GLenum usage) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bufferData(
+    GLenum target,
+    MaybeShared<DOMArrayBufferView> data,
+    GLenum usage) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bufferSubData(
+    GLenum target,
+    int64_t offset,
+    base::span<const uint8_t> data) {
+  NOTIMPLEMENTED();
+}
+
+GLenum WebGLRenderingContextWebGPUBase::checkFramebufferStatus(GLenum target) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+void WebGLRenderingContextWebGPUBase::clear(GLbitfield mask) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::clearColor(GLfloat red,
+                                                 GLfloat green,
+                                                 GLfloat blue,
+                                                 GLfloat alpha) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::clearDepth(GLfloat) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::clearStencil(GLint) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::colorMask(GLboolean red,
+                                                GLboolean green,
+                                                GLboolean blue,
+                                                GLboolean alpha) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compileShader(WebGLShader*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compressedTexImage2D(
+    GLenum target,
+    GLint level,
+    GLenum internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    MaybeShared<DOMArrayBufferView> data) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compressedTexSubImage2D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    MaybeShared<DOMArrayBufferView> data) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::copyTexImage2D(GLenum target,
+                                                     GLint level,
+                                                     GLenum internalformat,
+                                                     GLint x,
+                                                     GLint y,
+                                                     GLsizei width,
+                                                     GLsizei height,
+                                                     GLint border) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::copyTexSubImage2D(GLenum target,
+                                                        GLint level,
+                                                        GLint xoffset,
+                                                        GLint yoffset,
+                                                        GLint x,
+                                                        GLint y,
+                                                        GLsizei width,
+                                                        GLsizei height) {
+  NOTIMPLEMENTED();
+}
+
+WebGLBuffer* WebGLRenderingContextWebGPUBase::createBuffer() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+WebGLFramebuffer* WebGLRenderingContextWebGPUBase::createFramebuffer() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+WebGLProgram* WebGLRenderingContextWebGPUBase::createProgram() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+WebGLRenderbuffer* WebGLRenderingContextWebGPUBase::createRenderbuffer() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+WebGLShader* WebGLRenderingContextWebGPUBase::createShader(GLenum type) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+WebGLTexture* WebGLRenderingContextWebGPUBase::createTexture() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+void WebGLRenderingContextWebGPUBase::cullFace(GLenum mode) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::deleteBuffer(WebGLBuffer*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::deleteFramebuffer(WebGLFramebuffer*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::deleteProgram(WebGLProgram*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::deleteRenderbuffer(WebGLRenderbuffer*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::deleteShader(WebGLShader*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::deleteTexture(WebGLTexture*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::depthFunc(GLenum) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::depthMask(GLboolean) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::depthRange(GLfloat z_near,
+                                                 GLfloat z_far) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::detachShader(WebGLProgram*,
+                                                   WebGLShader*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::disable(GLenum cap) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::disableVertexAttribArray(GLuint index) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::drawArrays(GLenum mode,
+                                                 GLint first,
+                                                 GLsizei count) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::drawElements(GLenum mode,
+                                                   GLsizei count,
+                                                   GLenum type,
+                                                   int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::enable(GLenum cap) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::enableVertexAttribArray(GLuint index) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::finish() {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::flush() {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::framebufferRenderbuffer(
+    GLenum target,
+    GLenum attachment,
+    GLenum renderbuffertarget,
+    WebGLRenderbuffer*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::framebufferTexture2D(GLenum target,
+                                                           GLenum attachment,
+                                                           GLenum textarget,
+                                                           WebGLTexture*,
+                                                           GLint level) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::frontFace(GLenum mode) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::generateMipmap(GLenum target) {
+  NOTIMPLEMENTED();
+}
+
+WebGLActiveInfo* WebGLRenderingContextWebGPUBase::getActiveAttrib(
+    WebGLProgram*,
+    GLuint index) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+WebGLActiveInfo* WebGLRenderingContextWebGPUBase::getActiveUniform(
+    WebGLProgram*,
+    GLuint index) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+std::optional<HeapVector<Member<WebGLShader>>>
+WebGLRenderingContextWebGPUBase::getAttachedShaders(WebGLProgram*) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+GLint WebGLRenderingContextWebGPUBase::getAttribLocation(WebGLProgram*,
+                                                         const String& name) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getBufferParameter(ScriptState*,
+                                                                GLenum target,
+                                                                GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+WebGLContextAttributes* WebGLRenderingContextWebGPUBase::getContextAttributes()
+    const {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+GLenum WebGLRenderingContextWebGPUBase::getError() {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+ScriptObject WebGLRenderingContextWebGPUBase::getExtension(ScriptState*,
+                                                           const String& name) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getFramebufferAttachmentParameter(
+    ScriptState*,
+    GLenum target,
+    GLenum attachment,
+    GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getParameter(ScriptState*,
+                                                          GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getProgramParameter(ScriptState*,
+                                                                 WebGLProgram*,
+                                                                 GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+String WebGLRenderingContextWebGPUBase::getProgramInfoLog(WebGLProgram*) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getRenderbufferParameter(
+    ScriptState*,
+    GLenum target,
+    GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getShaderParameter(ScriptState*,
+                                                                WebGLShader*,
+                                                                GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+String WebGLRenderingContextWebGPUBase::getShaderInfoLog(WebGLShader*) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+WebGLShaderPrecisionFormat*
+WebGLRenderingContextWebGPUBase::getShaderPrecisionFormat(
+    GLenum shader_type,
+    GLenum precision_type) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+String WebGLRenderingContextWebGPUBase::getShaderSource(WebGLShader*) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+std::optional<Vector<String>>
+WebGLRenderingContextWebGPUBase::getSupportedExtensions() {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getTexParameter(ScriptState*,
+                                                             GLenum target,
+                                                             GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getUniform(
+    ScriptState*,
+    WebGLProgram*,
+    const WebGLUniformLocation*) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+WebGLUniformLocation* WebGLRenderingContextWebGPUBase::getUniformLocation(
+    WebGLProgram*,
+    const String&) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getVertexAttrib(ScriptState*,
+                                                             GLuint index,
+                                                             GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+int64_t WebGLRenderingContextWebGPUBase::getVertexAttribOffset(GLuint index,
+                                                               GLenum pname) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+void WebGLRenderingContextWebGPUBase::hint(GLenum target, GLenum mode) {
+  NOTIMPLEMENTED();
+}
+
+bool WebGLRenderingContextWebGPUBase::isBuffer(WebGLBuffer*) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool WebGLRenderingContextWebGPUBase::isEnabled(GLenum cap) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool WebGLRenderingContextWebGPUBase::isFramebuffer(WebGLFramebuffer*) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool WebGLRenderingContextWebGPUBase::isProgram(WebGLProgram*) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool WebGLRenderingContextWebGPUBase::isRenderbuffer(WebGLRenderbuffer*) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool WebGLRenderingContextWebGPUBase::isShader(WebGLShader*) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool WebGLRenderingContextWebGPUBase::isTexture(WebGLTexture*) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void WebGLRenderingContextWebGPUBase::lineWidth(GLfloat) {
+  NOTIMPLEMENTED();
+}
+void WebGLRenderingContextWebGPUBase::linkProgram(WebGLProgram*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::pixelStorei(GLenum pname, GLint param) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::polygonOffset(GLfloat factor,
+                                                    GLfloat units) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::readPixels(
+    GLint x,
+    GLint y,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    MaybeShared<DOMArrayBufferView> pixels) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::renderbufferStorage(GLenum target,
+                                                          GLenum internalformat,
+                                                          GLsizei width,
+                                                          GLsizei height) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::sampleCoverage(GLfloat value,
+                                                     GLboolean invert) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::scissor(GLint x,
+                                              GLint y,
+                                              GLsizei width,
+                                              GLsizei height) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::shaderSource(WebGLShader*,
+                                                   const String&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::stencilFunc(GLenum func,
+                                                  GLint ref,
+                                                  GLuint mask) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::stencilFuncSeparate(GLenum face,
+                                                          GLenum func,
+                                                          GLint ref,
+                                                          GLuint mask) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::stencilMask(GLuint) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::stencilMaskSeparate(GLenum face,
+                                                          GLuint mask) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::stencilOp(GLenum fail,
+                                                GLenum zfail,
+                                                GLenum zpass) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::stencilOpSeparate(GLenum face,
+                                                        GLenum fail,
+                                                        GLenum zfail,
+                                                        GLenum zpass) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texParameterf(GLenum target,
+                                                    GLenum pname,
+                                                    GLfloat param) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texParameteri(GLenum target,
+                                                    GLenum pname,
+                                                    GLint param) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    MaybeShared<DOMArrayBufferView>) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(GLenum target,
+                                                 GLint level,
+                                                 GLint internalformat,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 ImageData*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(ScriptState*,
+                                                 GLenum target,
+                                                 GLint level,
+                                                 GLint internalformat,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 HTMLImageElement*,
+                                                 ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(ScriptState*,
+                                                 GLenum target,
+                                                 GLint level,
+                                                 GLint internalformat,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 CanvasRenderingContextHost*,
+                                                 ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(ScriptState*,
+                                                 GLenum target,
+                                                 GLint level,
+                                                 GLint internalformat,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 HTMLVideoElement*,
+                                                 ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(ScriptState*,
+                                                 GLenum target,
+                                                 GLint level,
+                                                 GLint internalformat,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 VideoFrame*,
+                                                 ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(GLenum target,
+                                                 GLint level,
+                                                 GLint internalformat,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 ImageBitmap*,
+                                                 ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    MaybeShared<DOMArrayBufferView>) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(GLenum target,
+                                                    GLint level,
+                                                    GLint xoffset,
+                                                    GLint yoffset,
+                                                    GLenum format,
+                                                    GLenum type,
+                                                    ImageData*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(ScriptState*,
+                                                    GLenum target,
+                                                    GLint level,
+                                                    GLint xoffset,
+                                                    GLint yoffset,
+                                                    GLenum format,
+                                                    GLenum type,
+                                                    HTMLImageElement*,
+                                                    ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(ScriptState*,
+                                                    GLenum target,
+                                                    GLint level,
+                                                    GLint xoffset,
+                                                    GLint yoffset,
+                                                    GLenum format,
+                                                    GLenum type,
+                                                    CanvasRenderingContextHost*,
+                                                    ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+void WebGLRenderingContextWebGPUBase::texSubImage2D(ScriptState*,
+                                                    GLenum target,
+                                                    GLint level,
+                                                    GLint xoffset,
+                                                    GLint yoffset,
+                                                    GLenum format,
+                                                    GLenum type,
+                                                    HTMLVideoElement*,
+                                                    ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(ScriptState*,
+                                                    GLenum target,
+                                                    GLint level,
+                                                    GLint xoffset,
+                                                    GLint yoffset,
+                                                    GLenum format,
+                                                    GLenum type,
+                                                    VideoFrame*,
+                                                    ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(GLenum target,
+                                                    GLint level,
+                                                    GLint xoffset,
+                                                    GLint yoffset,
+                                                    GLenum format,
+                                                    GLenum type,
+                                                    ImageBitmap*,
+                                                    ExceptionState&) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform1f(const WebGLUniformLocation*,
+                                                GLfloat x) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform1fv(const WebGLUniformLocation*,
+                                                 base::span<const GLfloat>) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform1i(const WebGLUniformLocation*,
+                                                GLint x) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform1iv(const WebGLUniformLocation*,
+                                                 base::span<const GLint>) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform2f(const WebGLUniformLocation*,
+                                                GLfloat x,
+                                                GLfloat y) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform2fv(const WebGLUniformLocation*,
+                                                 base::span<const GLfloat>) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform2i(const WebGLUniformLocation*,
+                                                GLint x,
+                                                GLint y) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform2iv(const WebGLUniformLocation*,
+                                                 base::span<const GLint>) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform3f(const WebGLUniformLocation*,
+                                                GLfloat x,
+                                                GLfloat y,
+                                                GLfloat z) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform3fv(const WebGLUniformLocation*,
+                                                 base::span<const GLfloat>) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform3i(const WebGLUniformLocation*,
+                                                GLint x,
+                                                GLint y,
+                                                GLint z) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform3iv(const WebGLUniformLocation*,
+                                                 base::span<const GLint>) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform4f(const WebGLUniformLocation*,
+                                                GLfloat x,
+                                                GLfloat y,
+                                                GLfloat z,
+                                                GLfloat w) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform4fv(const WebGLUniformLocation*,
+                                                 base::span<const GLfloat>) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform4i(const WebGLUniformLocation*,
+                                                GLint x,
+                                                GLint y,
+                                                GLint z,
+                                                GLint w) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform4iv(const WebGLUniformLocation*,
+                                                 base::span<const GLint>) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix2fv(
+    const WebGLUniformLocation*,
+    GLboolean transpose,
+    base::span<const GLfloat> value) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix3fv(
+    const WebGLUniformLocation*,
+    GLboolean transpose,
+    base::span<const GLfloat> value) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix4fv(
+    const WebGLUniformLocation*,
+    GLboolean transpose,
+    base::span<const GLfloat> value) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::useProgram(WebGLProgram*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::validateProgram(WebGLProgram*) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttrib1f(GLuint index, GLfloat x) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttrib1fv(
+    GLuint index,
+    base::span<const GLfloat> values) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttrib2f(GLuint index,
+                                                     GLfloat x,
+                                                     GLfloat y) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttrib2fv(
+    GLuint index,
+    base::span<const GLfloat> values) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttrib3f(GLuint index,
+                                                     GLfloat x,
+                                                     GLfloat y,
+                                                     GLfloat z) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttrib3fv(
+    GLuint index,
+    base::span<const GLfloat> values) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttrib4f(GLuint index,
+                                                     GLfloat x,
+                                                     GLfloat y,
+                                                     GLfloat z,
+                                                     GLfloat w) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttrib4fv(
+    GLuint index,
+    base::span<const GLfloat> values) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttribPointer(GLuint index,
+                                                          GLint size,
+                                                          GLenum type,
+                                                          GLboolean normalized,
+                                                          GLsizei stride,
+                                                          int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::viewport(GLint x,
+                                               GLint y,
+                                               GLsizei width,
+                                               GLsizei height) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::drawingBufferStorage(GLenum sizedformat,
+                                                           GLsizei width,
+                                                           GLsizei height) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::commit() {
+  NOTIMPLEMENTED();
+}
+
+ScriptPromise<IDLUndefined> WebGLRenderingContextWebGPUBase::makeXRCompatible(
+    ScriptState*,
+    ExceptionState&) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+// ****************************************************************************
+// End of WebGLRenderingContextBase's IDL methods
+// ****************************************************************************
+
+// **************************************************************************
+// Start of WebGL2RenderingContextBase's IDL methods
+// **************************************************************************
+
+void WebGLRenderingContextWebGPUBase::bufferData(
+    GLenum target,
+    MaybeShared<DOMArrayBufferView> srcData,
+    GLenum usage,
+    int64_t srcOffset,
+    GLuint length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bufferSubData(
+    GLenum target,
+    int64_t offset,
+    MaybeShared<DOMArrayBufferView> srcData,
+    int64_t srcOffset,
+    GLuint length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::copyBufferSubData(GLenum readTarget,
+                                                        GLenum writeTarget,
+                                                        int64_t readOffset,
+                                                        int64_t writeOffset,
+                                                        int64_t size) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::getBufferSubData(
+    GLenum target,
+    int64_t srcByteOffset,
+    MaybeShared<DOMArrayBufferView> dstData,
+    int64_t dstOffset,
+    GLuint length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::blitFramebuffer(GLint src_x0,
+                                                      GLint src_y0,
+                                                      GLint src_x1,
+                                                      GLint src_y1,
+                                                      GLint dst_x0,
+                                                      GLint dst_y0,
+                                                      GLint dst_x1,
+                                                      GLint dst_y1,
+                                                      GLbitfield mask,
+                                                      GLenum filter) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::framebufferTextureLayer(
+    GLenum target,
+    GLenum attachment,
+    WebGLTexture* texture,
+    GLint level,
+    GLint layer) {
+  NOTIMPLEMENTED();
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getInternalformatParameter(
+    ScriptState* script_state,
+    GLenum target,
+    GLenum internalformat,
+    GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+void WebGLRenderingContextWebGPUBase::invalidateFramebuffer(
+    GLenum target,
+    const Vector<GLenum>& attachments) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::invalidateSubFramebuffer(
+    GLenum target,
+    const Vector<GLenum>& attachments,
+    GLint x,
+    GLint y,
+    GLsizei width,
+    GLsizei height) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::readBuffer(GLenum mode) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::renderbufferStorageMultisample(
+    GLenum target,
+    GLsizei samples,
+    GLenum internalformat,
+    GLsizei width,
+    GLsizei height) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(GLenum target,
+                                                 GLint level,
+                                                 GLint internalformat,
+                                                 GLsizei width,
+                                                 GLsizei height,
+                                                 GLint border,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(GLenum target,
+                                                 GLint level,
+                                                 GLint internalformat,
+                                                 GLsizei width,
+                                                 GLsizei height,
+                                                 GLint border,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 ImageData* pixels) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    HTMLImageElement* image,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    CanvasRenderingContextHost* canvas,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    HTMLVideoElement* video,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    VideoFrame* frame,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    ImageBitmap* bitmap,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage2D(
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    MaybeShared<DOMArrayBufferView> data,
+    int64_t src_offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texElement2D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLenum format,
+    GLenum type,
+    Element* element,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(GLenum target,
+                                                    GLint level,
+                                                    GLint xoffset,
+                                                    GLint yoffset,
+                                                    GLsizei width,
+                                                    GLsizei height,
+                                                    GLenum format,
+                                                    GLenum type,
+                                                    int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(GLenum target,
+                                                    GLint level,
+                                                    GLint xoffset,
+                                                    GLint yoffset,
+                                                    GLsizei width,
+                                                    GLsizei height,
+                                                    GLenum format,
+                                                    GLenum type,
+                                                    ImageData* pixels) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    HTMLImageElement* image,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    CanvasRenderingContextHost* canvas,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    HTMLVideoElement* video,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    VideoFrame* frame,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    ImageBitmap* bitmap,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage2D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    MaybeShared<DOMArrayBufferView> pixels,
+    int64_t src_offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texStorage2D(GLenum target,
+                                                   GLsizei levels,
+                                                   GLenum internalformat,
+                                                   GLsizei width,
+                                                   GLsizei height) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texStorage3D(GLenum target,
+                                                   GLsizei levels,
+                                                   GLenum internalformat,
+                                                   GLsizei width,
+                                                   GLsizei height,
+                                                   GLsizei depth) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage3D(GLenum target,
+                                                 GLint level,
+                                                 GLint internalformat,
+                                                 GLsizei width,
+                                                 GLsizei height,
+                                                 GLsizei depth,
+                                                 GLint border,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage3D(GLenum target,
+                                                 GLint level,
+                                                 GLint internalformat,
+                                                 GLsizei width,
+                                                 GLsizei height,
+                                                 GLsizei depth,
+                                                 GLint border,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 ImageData* pixels) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage3D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    HTMLImageElement* image,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage3D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    CanvasRenderingContextHost* canvas,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage3D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    HTMLVideoElement* video,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage3D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    VideoFrame* frame,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage3D(
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    ImageBitmap* bitmap,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage3D(
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    MaybeShared<DOMArrayBufferView> pixels) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texImage3D(
+    GLenum target,
+    GLint level,
+    GLint internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    GLenum format,
+    GLenum type,
+    MaybeShared<DOMArrayBufferView> pixels,
+    GLuint src_offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage3D(GLenum target,
+                                                    GLint level,
+                                                    GLint xoffset,
+                                                    GLint yoffset,
+                                                    GLint zoffset,
+                                                    GLsizei width,
+                                                    GLsizei height,
+                                                    GLsizei depth,
+                                                    GLenum format,
+                                                    GLenum type,
+                                                    int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage3D(GLenum target,
+                                                    GLint level,
+                                                    GLint xoffset,
+                                                    GLint yoffset,
+                                                    GLint zoffset,
+                                                    GLsizei width,
+                                                    GLsizei height,
+                                                    GLsizei depth,
+                                                    GLenum format,
+                                                    GLenum type,
+                                                    ImageData* pixels) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage3D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint zoffset,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLenum format,
+    GLenum type,
+    HTMLImageElement* image,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage3D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint zoffset,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLenum format,
+    GLenum type,
+    CanvasRenderingContextHost* context_host,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage3D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint zoffset,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLenum format,
+    GLenum type,
+    HTMLVideoElement* video,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage3D(
+    ScriptState* script_state,
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint zoffset,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLenum format,
+    GLenum type,
+    VideoFrame* frame,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage3D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint zoffset,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLenum format,
+    GLenum type,
+    ImageBitmap* bitmap,
+    ExceptionState& exception_state) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage3D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint zoffset,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLenum format,
+    GLenum type,
+    MaybeShared<DOMArrayBufferView> pixels) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::texSubImage3D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint zoffset,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLenum format,
+    GLenum type,
+    MaybeShared<DOMArrayBufferView> pixels,
+    GLuint src_offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::copyTexSubImage3D(GLenum target,
+                                                        GLint level,
+                                                        GLint xoffset,
+                                                        GLint yoffset,
+                                                        GLint zoffset,
+                                                        GLint x,
+                                                        GLint y,
+                                                        GLsizei width,
+                                                        GLsizei height) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compressedTexImage2D(
+    GLenum target,
+    GLint level,
+    GLenum internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    MaybeShared<DOMArrayBufferView> data,
+    GLuint src_offset,
+    GLuint src_length_override) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compressedTexSubImage2D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    MaybeShared<DOMArrayBufferView> data,
+    GLuint src_offset,
+    GLuint src_length_override) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compressedTexImage3D(
+    GLenum target,
+    GLint level,
+    GLenum internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    MaybeShared<DOMArrayBufferView> data,
+    GLuint src_offset,
+    GLuint src_length_override) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compressedTexSubImage3D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint zoffset,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLenum format,
+    MaybeShared<DOMArrayBufferView> data,
+    GLuint src_offset,
+    GLuint src_length_override) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compressedTexImage2D(
+    GLenum target,
+    GLint level,
+    GLenum internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLint border,
+    GLsizei image_size,
+    int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compressedTexSubImage2D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLsizei image_size,
+    int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compressedTexImage3D(
+    GLenum target,
+    GLint level,
+    GLenum internalformat,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLint border,
+    GLsizei image_size,
+    int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::compressedTexSubImage3D(
+    GLenum target,
+    GLint level,
+    GLint xoffset,
+    GLint yoffset,
+    GLint zoffset,
+    GLsizei width,
+    GLsizei height,
+    GLsizei depth,
+    GLenum format,
+    GLsizei image_size,
+    int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+GLint WebGLRenderingContextWebGPUBase::getFragDataLocation(
+    WebGLProgram* program,
+    const String& name) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+void WebGLRenderingContextWebGPUBase::uniform1ui(
+    const WebGLUniformLocation* location,
+    GLuint v0) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform2ui(
+    const WebGLUniformLocation* location,
+    GLuint v0,
+    GLuint v1) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform3ui(
+    const WebGLUniformLocation* location,
+    GLuint v0,
+    GLuint v1,
+    GLuint v2) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform4ui(
+    const WebGLUniformLocation* location,
+    GLuint v0,
+    GLuint v1,
+    GLuint v2,
+    GLuint v3) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform1fv(
+    const WebGLUniformLocation* location,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform2fv(
+    const WebGLUniformLocation* location,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform3fv(
+    const WebGLUniformLocation* location,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform4fv(
+    const WebGLUniformLocation* location,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform1iv(
+    const WebGLUniformLocation* location,
+    base::span<const GLint> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform2iv(
+    const WebGLUniformLocation* location,
+    base::span<const GLint> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform3iv(
+    const WebGLUniformLocation* location,
+    base::span<const GLint> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform4iv(
+    const WebGLUniformLocation* location,
+    base::span<const GLint> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform1uiv(
+    const WebGLUniformLocation* location,
+    base::span<const GLuint> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform2uiv(
+    const WebGLUniformLocation* location,
+    base::span<const GLuint> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform3uiv(
+    const WebGLUniformLocation* location,
+    base::span<const GLuint> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniform4uiv(
+    const WebGLUniformLocation* location,
+    base::span<const GLuint> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix2fv(
+    const WebGLUniformLocation* location,
+    GLboolean transpose,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix3fv(
+    const WebGLUniformLocation* location,
+    GLboolean transpose,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix4fv(
+    const WebGLUniformLocation* location,
+    GLboolean transpose,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix2x3fv(
+    const WebGLUniformLocation* location,
+    GLboolean transpose,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix3x2fv(
+    const WebGLUniformLocation* location,
+    GLboolean transpose,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix2x4fv(
+    const WebGLUniformLocation* location,
+    GLboolean transpose,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix4x2fv(
+    const WebGLUniformLocation* location,
+    GLboolean transpose,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix3x4fv(
+    const WebGLUniformLocation* location,
+    GLboolean transpose,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::uniformMatrix4x3fv(
+    const WebGLUniformLocation* location,
+    GLboolean transpose,
+    base::span<const GLfloat> v,
+    GLuint src_offset,
+    GLuint src_length) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttribI4i(GLuint index,
+                                                      GLint x,
+                                                      GLint y,
+                                                      GLint z,
+                                                      GLint w) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttribI4iv(
+    GLuint index,
+    base::span<const GLint> v) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttribI4ui(GLuint index,
+                                                       GLuint x,
+                                                       GLuint y,
+                                                       GLuint z,
+                                                       GLuint w) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttribI4uiv(
+    GLuint index,
+    base::span<const GLuint> v) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttribIPointer(GLuint index,
+                                                           GLint size,
+                                                           GLenum type,
+                                                           GLsizei stride,
+                                                           int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::vertexAttribDivisor(GLuint index,
+                                                          GLuint divisor) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::drawArraysInstanced(
+    GLenum mode,
+    GLint first,
+    GLsizei count,
+    GLsizei instance_count) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::drawElementsInstanced(
+    GLenum mode,
+    GLsizei count,
+    GLenum type,
+    int64_t offset,
+    GLsizei instance_count) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::drawRangeElements(GLenum mode,
+                                                        GLuint start,
+                                                        GLuint end,
+                                                        GLsizei count,
+                                                        GLenum type,
+                                                        int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::drawBuffers(
+    const Vector<GLenum>& buffers) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::clearBufferiv(
+    GLenum buffer,
+    GLint drawbuffer,
+    base::span<const GLint> value,
+    GLuint src_offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::clearBufferuiv(
+    GLenum buffer,
+    GLint drawbuffer,
+    base::span<const GLuint> value,
+    GLuint src_offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::clearBufferfv(
+    GLenum buffer,
+    GLint drawbuffer,
+    base::span<const GLfloat> value,
+    GLuint src_offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::clearBufferfi(GLenum buffer,
+                                                    GLint drawbuffer,
+                                                    GLfloat depth,
+                                                    GLint stencil) {
+  NOTIMPLEMENTED();
+}
+
+WebGLQuery* WebGLRenderingContextWebGPUBase::createQuery() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+void WebGLRenderingContextWebGPUBase::deleteQuery(WebGLQuery* query) {
+  NOTIMPLEMENTED();
+}
+
+bool WebGLRenderingContextWebGPUBase::isQuery(WebGLQuery* query) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void WebGLRenderingContextWebGPUBase::beginQuery(GLenum target,
+                                                 WebGLQuery* query) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::endQuery(GLenum target) {
+  NOTIMPLEMENTED();
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getQuery(ScriptState* script_state,
+                                                      GLenum target,
+                                                      GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getQueryParameter(
+    ScriptState* script_state,
+    WebGLQuery* query,
+    GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+WebGLSampler* WebGLRenderingContextWebGPUBase::createSampler() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+void WebGLRenderingContextWebGPUBase::deleteSampler(WebGLSampler* sampler) {
+  NOTIMPLEMENTED();
+}
+
+bool WebGLRenderingContextWebGPUBase::isSampler(WebGLSampler* sampler) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void WebGLRenderingContextWebGPUBase::bindSampler(GLuint unit,
+                                                  WebGLSampler* sampler) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::samplerParameteri(WebGLSampler* sampler,
+                                                        GLenum pname,
+                                                        GLint param) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::samplerParameterf(WebGLSampler* sampler,
+                                                        GLenum pname,
+                                                        GLfloat param) {
+  NOTIMPLEMENTED();
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getSamplerParameter(
+    ScriptState* script_state,
+    WebGLSampler* sampler,
+    GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+WebGLSync* WebGLRenderingContextWebGPUBase::fenceSync(GLenum condition,
+                                                      GLbitfield flags) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+bool WebGLRenderingContextWebGPUBase::isSync(WebGLSync* sync) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void WebGLRenderingContextWebGPUBase::deleteSync(WebGLSync* sync) {
+  NOTIMPLEMENTED();
+}
+
+GLenum WebGLRenderingContextWebGPUBase::clientWaitSync(WebGLSync* sync,
+                                                       GLbitfield flags,
+                                                       GLuint64 timeout) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+void WebGLRenderingContextWebGPUBase::waitSync(WebGLSync* sync,
+                                               GLbitfield flags,
+                                               GLint64 timeout) {
+  NOTIMPLEMENTED();
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getSyncParameter(
+    ScriptState* script_state,
+    WebGLSync* sync,
+    GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+WebGLTransformFeedback*
+WebGLRenderingContextWebGPUBase::createTransformFeedback() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+void WebGLRenderingContextWebGPUBase::deleteTransformFeedback(
+    WebGLTransformFeedback* feedback) {
+  NOTIMPLEMENTED();
+}
+
+bool WebGLRenderingContextWebGPUBase::isTransformFeedback(
+    WebGLTransformFeedback* feedback) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void WebGLRenderingContextWebGPUBase::bindTransformFeedback(
+    GLenum target,
+    WebGLTransformFeedback* feedback) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::beginTransformFeedback(
+    GLenum primitive_mode) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::endTransformFeedback() {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::transformFeedbackVaryings(
+    WebGLProgram* program,
+    const Vector<String>& varyings,
+    GLenum buffer_mode) {
+  NOTIMPLEMENTED();
+}
+
+WebGLActiveInfo* WebGLRenderingContextWebGPUBase::getTransformFeedbackVarying(
+    WebGLProgram* program,
+    GLuint index) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+void WebGLRenderingContextWebGPUBase::pauseTransformFeedback() {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::resumeTransformFeedback() {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bindBufferBase(GLenum target,
+                                                     GLuint index,
+                                                     WebGLBuffer* buffer) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::bindBufferRange(GLenum target,
+                                                      GLuint index,
+                                                      WebGLBuffer* buffer,
+                                                      int64_t offset,
+                                                      int64_t size) {
+  NOTIMPLEMENTED();
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getIndexedParameter(
+    ScriptState* script_state,
+    GLenum target,
+    GLuint index) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+std::optional<Vector<GLuint>>
+WebGLRenderingContextWebGPUBase::getUniformIndices(
+    WebGLProgram* program,
+    const Vector<String>& uniform_names) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getActiveUniforms(
+    ScriptState* script_state,
+    WebGLProgram* program,
+    const Vector<GLuint>& uniform_indices,
+    GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+GLuint WebGLRenderingContextWebGPUBase::getUniformBlockIndex(
+    WebGLProgram* program,
+    const String& uniform_block_name) {
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+ScriptValue WebGLRenderingContextWebGPUBase::getActiveUniformBlockParameter(
+    ScriptState* script_state,
+    WebGLProgram* program,
+    GLuint uniform_block_index,
+    GLenum pname) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+String WebGLRenderingContextWebGPUBase::getActiveUniformBlockName(
+    WebGLProgram* program,
+    GLuint uniform_block_index) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+void WebGLRenderingContextWebGPUBase::uniformBlockBinding(
+    WebGLProgram* program,
+    GLuint uniform_block_index,
+    GLuint uniform_block_binding) {
+  NOTIMPLEMENTED();
+}
+
+WebGLVertexArrayObject* WebGLRenderingContextWebGPUBase::createVertexArray() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+void WebGLRenderingContextWebGPUBase::deleteVertexArray(
+    WebGLVertexArrayObject* vertex_array) {
+  NOTIMPLEMENTED();
+}
+
+bool WebGLRenderingContextWebGPUBase::isVertexArray(
+    WebGLVertexArrayObject* vertex_array) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void WebGLRenderingContextWebGPUBase::bindVertexArray(
+    WebGLVertexArrayObject* vertex_array) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::readPixels(GLint x,
+                                                 GLint y,
+                                                 GLsizei width,
+                                                 GLsizei height,
+                                                 GLenum format,
+                                                 GLenum type,
+                                                 int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::readPixels(
+    GLint x,
+    GLint y,
+    GLsizei width,
+    GLsizei height,
+    GLenum format,
+    GLenum type,
+    MaybeShared<DOMArrayBufferView> pixels,
+    int64_t offset) {
+  NOTIMPLEMENTED();
+}
+
+// **************************************************************************
+// End of WebGL2RenderingContextBase's IDL methods
+// **************************************************************************
+
+// ****************************************************************************
+// Start of CanvasRenderingContext implementation
+// ****************************************************************************
+SkAlphaType WebGLRenderingContextWebGPUBase::GetAlphaType() const {
+  NOTIMPLEMENTED();
+  return SkAlphaType::kUnknown_SkAlphaType;
+}
+
+viz::SharedImageFormat WebGLRenderingContextWebGPUBase::GetSharedImageFormat()
+    const {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+gfx::ColorSpace WebGLRenderingContextWebGPUBase::GetColorSpace() const {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+bool WebGLRenderingContextWebGPUBase::isContextLost() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+scoped_refptr<StaticBitmapImage> WebGLRenderingContextWebGPUBase::GetImage(
+    FlushReason) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+void WebGLRenderingContextWebGPUBase::SetHdrMetadata(
+    const gfx::HDRMetadata& hdr_metadata) {
+  NOTIMPLEMENTED();
+}
+
+bool WebGLRenderingContextWebGPUBase::IsComposited() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool WebGLRenderingContextWebGPUBase::IsPaintable() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool WebGLRenderingContextWebGPUBase::UsingSwapChain() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void WebGLRenderingContextWebGPUBase::PageVisibilityChanged() {
+  NOTIMPLEMENTED();
+}
+
+bool WebGLRenderingContextWebGPUBase::PaintRenderingResultsToCanvas(
+    SourceDrawingBuffer) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool WebGLRenderingContextWebGPUBase::CopyRenderingResultsToVideoFrame(
+    WebGraphicsContext3DVideoFramePool*,
+    SourceDrawingBuffer,
+    const gfx::ColorSpace&,
+    VideoFrameCopyCompletedCallback) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+cc::Layer* WebGLRenderingContextWebGPUBase::CcLayer() const {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+void WebGLRenderingContextWebGPUBase::Stop() {
+  NOTIMPLEMENTED();
+}
+
+void WebGLRenderingContextWebGPUBase::FinalizeFrame(FlushReason) {
+  NOTIMPLEMENTED();
+}
+
+bool WebGLRenderingContextWebGPUBase::PushFrame() {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+// ****************************************************************************
+// End of CanvasRenderingContext implementation
+// ****************************************************************************
+
+void WebGLRenderingContextWebGPUBase::Trace(Visitor* visitor) const {
+  WebGLContextObjectSupport::Trace(visitor);
+  CanvasRenderingContext::Trace(visitor);
+}
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.h
new file mode 100644
index 0000000..10df703f
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.h
@@ -0,0 +1,1285 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_RENDERING_CONTEXT_WEBGPU_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_RENDERING_CONTEXT_WEBGPU_BASE_H_
+
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_predefined_color_space.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_webgl_context_attributes.h"
+#include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
+#include "third_party/blink/renderer/modules/webgl/webgl_context_object_support.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class ExceptionState;
+class HTMLImageElement;
+class HTMLVideoElement;
+class ImageBitmap;
+class ImageData;
+class ScriptState;
+class V8PredefinedColorSpace;
+class V8UnionHTMLCanvasElementOrOffscreenCanvas;
+class VideoFrame;
+class WebGLActiveInfo;
+class WebGLBuffer;
+class WebGLFramebuffer;
+class WebGLProgram;
+class WebGLQuery;
+class WebGLRenderbuffer;
+class WebGLSampler;
+class WebGLShader;
+class WebGLShaderPrecisionFormat;
+class WebGLSync;
+class WebGLTexture;
+class WebGLTransformFeedback;
+class WebGLUniformLocation;
+class WebGLVertexArrayObject;
+
+class MODULES_EXPORT WebGLRenderingContextWebGPUBase
+    : public WebGLContextObjectSupport,
+      public CanvasRenderingContext {
+ public:
+  WebGLRenderingContextWebGPUBase(
+      CanvasRenderingContextHost* host,
+      const CanvasContextCreationAttributesCore& requested_attributes,
+      CanvasRenderingAPI api);
+  ~WebGLRenderingContextWebGPUBase() override;
+
+  WebGLRenderingContextWebGPUBase(const WebGLRenderingContextWebGPUBase&) =
+      delete;
+  WebGLRenderingContextWebGPUBase& operator=(
+      const WebGLRenderingContextWebGPUBase&) = delete;
+
+  // **************************************************************************
+  // Start of WebGLRenderingContextBase's IDL methods
+  // **************************************************************************
+  V8UnionHTMLCanvasElementOrOffscreenCanvas* getHTMLOrOffscreenCanvas() const;
+
+  int drawingBufferWidth() const;
+  int drawingBufferHeight() const;
+  GLenum drawingBufferFormat() const;
+  V8PredefinedColorSpace drawingBufferColorSpace() const;
+  void setDrawingBufferColorSpace(const V8PredefinedColorSpace& color_space,
+                                  ExceptionState&);
+  V8PredefinedColorSpace unpackColorSpace() const;
+  void setUnpackColorSpace(const V8PredefinedColorSpace& color_space,
+                           ExceptionState&);
+
+  void activeTexture(GLenum texture);
+  void attachShader(WebGLProgram*, WebGLShader*);
+
+  void bindAttribLocation(WebGLProgram*, GLuint index, const String& name);
+  void bindBuffer(GLenum target, WebGLBuffer* buffer);
+  void bindFramebuffer(GLenum target, WebGLFramebuffer*);
+  void bindRenderbuffer(GLenum target, WebGLRenderbuffer*);
+  void bindTexture(GLenum target, WebGLTexture*);
+  void blendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+  void blendEquation(GLenum mode);
+  void blendEquationSeparate(GLenum mode_rgb, GLenum mode_alpha);
+  void blendFunc(GLenum sfactor, GLenum dfactor);
+  void blendFuncSeparate(GLenum src_rgb,
+                         GLenum dst_rgb,
+                         GLenum src_alpha,
+                         GLenum dst_alpha);
+
+  void bufferData(GLenum target, int64_t size, GLenum usage);
+  void bufferData(GLenum target, DOMArrayBufferBase* data, GLenum usage);
+  void bufferData(GLenum target,
+                  MaybeShared<DOMArrayBufferView> data,
+                  GLenum usage);
+  void bufferSubData(GLenum target,
+                     int64_t offset,
+                     base::span<const uint8_t> data);
+
+  GLenum checkFramebufferStatus(GLenum target);
+  void clear(GLbitfield mask);
+  void clearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+  void clearDepth(GLfloat);
+  void clearStencil(GLint);
+  void colorMask(GLboolean red,
+                 GLboolean green,
+                 GLboolean blue,
+                 GLboolean alpha);
+  void compileShader(WebGLShader*);
+
+  void compressedTexImage2D(GLenum target,
+                            GLint level,
+                            GLenum internalformat,
+                            GLsizei width,
+                            GLsizei height,
+                            GLint border,
+                            MaybeShared<DOMArrayBufferView> data);
+  void compressedTexSubImage2D(GLenum target,
+                               GLint level,
+                               GLint xoffset,
+                               GLint yoffset,
+                               GLsizei width,
+                               GLsizei height,
+                               GLenum format,
+                               MaybeShared<DOMArrayBufferView> data);
+  void copyTexImage2D(GLenum target,
+                      GLint level,
+                      GLenum internalformat,
+                      GLint x,
+                      GLint y,
+                      GLsizei width,
+                      GLsizei height,
+                      GLint border);
+  void copyTexSubImage2D(GLenum target,
+                         GLint level,
+                         GLint xoffset,
+                         GLint yoffset,
+                         GLint x,
+                         GLint y,
+                         GLsizei width,
+                         GLsizei height);
+
+  WebGLBuffer* createBuffer();
+  WebGLFramebuffer* createFramebuffer();
+  WebGLProgram* createProgram();
+  WebGLRenderbuffer* createRenderbuffer();
+  WebGLShader* createShader(GLenum type);
+  WebGLTexture* createTexture();
+
+  void cullFace(GLenum mode);
+
+  void deleteBuffer(WebGLBuffer*);
+  void deleteFramebuffer(WebGLFramebuffer*);
+  void deleteProgram(WebGLProgram*);
+  void deleteRenderbuffer(WebGLRenderbuffer*);
+  void deleteShader(WebGLShader*);
+  void deleteTexture(WebGLTexture*);
+
+  void depthFunc(GLenum);
+  void depthMask(GLboolean);
+  void depthRange(GLfloat z_near, GLfloat z_far);
+  void detachShader(WebGLProgram*, WebGLShader*);
+  void disable(GLenum cap);
+  void disableVertexAttribArray(GLuint index);
+  void drawArrays(GLenum mode, GLint first, GLsizei count);
+  void drawElements(GLenum mode, GLsizei count, GLenum type, int64_t offset);
+
+  void enable(GLenum cap);
+  void enableVertexAttribArray(GLuint index);
+  void finish();
+  void flush();
+  void framebufferRenderbuffer(GLenum target,
+                               GLenum attachment,
+                               GLenum renderbuffertarget,
+                               WebGLRenderbuffer*);
+  void framebufferTexture2D(GLenum target,
+                            GLenum attachment,
+                            GLenum textarget,
+                            WebGLTexture*,
+                            GLint level);
+  void frontFace(GLenum mode);
+  void generateMipmap(GLenum target);
+
+  WebGLActiveInfo* getActiveAttrib(WebGLProgram*, GLuint index);
+  WebGLActiveInfo* getActiveUniform(WebGLProgram*, GLuint index);
+  std::optional<HeapVector<Member<WebGLShader>>> getAttachedShaders(
+      WebGLProgram*);
+  GLint getAttribLocation(WebGLProgram*, const String& name);
+  ScriptValue getBufferParameter(ScriptState*, GLenum target, GLenum pname);
+  WebGLContextAttributes* getContextAttributes() const;
+  GLenum getError();
+  ScriptObject getExtension(ScriptState*, const String& name);
+  ScriptValue getFramebufferAttachmentParameter(ScriptState*,
+                                                GLenum target,
+                                                GLenum attachment,
+                                                GLenum pname);
+  ScriptValue getParameter(ScriptState*, GLenum pname);
+  ScriptValue getProgramParameter(ScriptState*, WebGLProgram*, GLenum pname);
+  String getProgramInfoLog(WebGLProgram*);
+  ScriptValue getRenderbufferParameter(ScriptState*,
+                                       GLenum target,
+                                       GLenum pname);
+  ScriptValue getShaderParameter(ScriptState*, WebGLShader*, GLenum pname);
+  String getShaderInfoLog(WebGLShader*);
+  WebGLShaderPrecisionFormat* getShaderPrecisionFormat(GLenum shader_type,
+                                                       GLenum precision_type);
+  String getShaderSource(WebGLShader*);
+  std::optional<Vector<String>> getSupportedExtensions();
+  ScriptValue getTexParameter(ScriptState*, GLenum target, GLenum pname);
+  ScriptValue getUniform(ScriptState*,
+                         WebGLProgram*,
+                         const WebGLUniformLocation*);
+  WebGLUniformLocation* getUniformLocation(WebGLProgram*, const String&);
+  ScriptValue getVertexAttrib(ScriptState*, GLuint index, GLenum pname);
+  int64_t getVertexAttribOffset(GLuint index, GLenum pname);
+
+  void hint(GLenum target, GLenum mode);
+  bool isBuffer(WebGLBuffer*);
+  bool isEnabled(GLenum cap);
+  bool isFramebuffer(WebGLFramebuffer*);
+  bool isProgram(WebGLProgram*);
+  bool isRenderbuffer(WebGLRenderbuffer*);
+  bool isShader(WebGLShader*);
+  bool isTexture(WebGLTexture*);
+
+  void lineWidth(GLfloat);
+  void linkProgram(WebGLProgram*);
+  void pixelStorei(GLenum pname, GLint param);
+  void polygonOffset(GLfloat factor, GLfloat units);
+
+  void readPixels(GLint x,
+                  GLint y,
+                  GLsizei width,
+                  GLsizei height,
+                  GLenum format,
+                  GLenum type,
+                  MaybeShared<DOMArrayBufferView> pixels);
+
+  void renderbufferStorage(GLenum target,
+                           GLenum internalformat,
+                           GLsizei width,
+                           GLsizei height);
+
+  void sampleCoverage(GLfloat value, GLboolean invert);
+  void scissor(GLint x, GLint y, GLsizei width, GLsizei height);
+  void shaderSource(WebGLShader*, const String&);
+  void stencilFunc(GLenum func, GLint ref, GLuint mask);
+  void stencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
+  void stencilMask(GLuint);
+  void stencilMaskSeparate(GLenum face, GLuint mask);
+  void stencilOp(GLenum fail, GLenum zfail, GLenum zpass);
+  void stencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
+
+  void texParameterf(GLenum target, GLenum pname, GLfloat param);
+  void texParameteri(GLenum target, GLenum pname, GLint param);
+
+  void texImage2D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  MaybeShared<DOMArrayBufferView>);
+  void texImage2D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLenum format,
+                  GLenum type,
+                  ImageData*);
+  void texImage2D(ScriptState*,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLenum format,
+                  GLenum type,
+                  HTMLImageElement*,
+                  ExceptionState&);
+  void texImage2D(ScriptState*,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLenum format,
+                  GLenum type,
+                  CanvasRenderingContextHost*,
+                  ExceptionState&);
+  void texImage2D(ScriptState*,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLenum format,
+                  GLenum type,
+                  HTMLVideoElement*,
+                  ExceptionState&);
+  void texImage2D(ScriptState*,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLenum format,
+                  GLenum type,
+                  VideoFrame*,
+                  ExceptionState&);
+  void texImage2D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLenum format,
+                  GLenum type,
+                  ImageBitmap*,
+                  ExceptionState&);
+
+  void texSubImage2D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     MaybeShared<DOMArrayBufferView>);
+  void texSubImage2D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLenum format,
+                     GLenum type,
+                     ImageData*);
+  void texSubImage2D(ScriptState*,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLenum format,
+                     GLenum type,
+                     HTMLImageElement*,
+                     ExceptionState&);
+  void texSubImage2D(ScriptState*,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLenum format,
+                     GLenum type,
+                     CanvasRenderingContextHost*,
+                     ExceptionState&);
+  void texSubImage2D(ScriptState*,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLenum format,
+                     GLenum type,
+                     HTMLVideoElement*,
+                     ExceptionState&);
+  void texSubImage2D(ScriptState*,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLenum format,
+                     GLenum type,
+                     VideoFrame*,
+                     ExceptionState&);
+  void texSubImage2D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLenum format,
+                     GLenum type,
+                     ImageBitmap*,
+                     ExceptionState&);
+
+  void uniform1f(const WebGLUniformLocation*, GLfloat x);
+  void uniform1fv(const WebGLUniformLocation*, base::span<const GLfloat>);
+  void uniform1i(const WebGLUniformLocation*, GLint x);
+  void uniform1iv(const WebGLUniformLocation*, base::span<const GLint>);
+  void uniform2f(const WebGLUniformLocation*, GLfloat x, GLfloat y);
+  void uniform2fv(const WebGLUniformLocation*, base::span<const GLfloat>);
+  void uniform2i(const WebGLUniformLocation*, GLint x, GLint y);
+  void uniform2iv(const WebGLUniformLocation*, base::span<const GLint>);
+  void uniform3f(const WebGLUniformLocation*, GLfloat x, GLfloat y, GLfloat z);
+  void uniform3fv(const WebGLUniformLocation*, base::span<const GLfloat>);
+  void uniform3i(const WebGLUniformLocation*, GLint x, GLint y, GLint z);
+  void uniform3iv(const WebGLUniformLocation*, base::span<const GLint>);
+  void uniform4f(const WebGLUniformLocation*,
+                 GLfloat x,
+                 GLfloat y,
+                 GLfloat z,
+                 GLfloat w);
+  void uniform4fv(const WebGLUniformLocation*, base::span<const GLfloat>);
+  void uniform4i(const WebGLUniformLocation*,
+                 GLint x,
+                 GLint y,
+                 GLint z,
+                 GLint w);
+  void uniform4iv(const WebGLUniformLocation*, base::span<const GLint>);
+
+  void uniformMatrix2fv(const WebGLUniformLocation*,
+                        GLboolean transpose,
+                        base::span<const GLfloat> value);
+  void uniformMatrix3fv(const WebGLUniformLocation*,
+                        GLboolean transpose,
+                        base::span<const GLfloat> value);
+  void uniformMatrix4fv(const WebGLUniformLocation*,
+                        GLboolean transpose,
+                        base::span<const GLfloat> value);
+
+  void useProgram(WebGLProgram*);
+  void validateProgram(WebGLProgram*);
+
+  void vertexAttrib1f(GLuint index, GLfloat x);
+  void vertexAttrib1fv(GLuint index, base::span<const GLfloat> values);
+  void vertexAttrib2f(GLuint index, GLfloat x, GLfloat y);
+  void vertexAttrib2fv(GLuint index, base::span<const GLfloat> values);
+  void vertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z);
+  void vertexAttrib3fv(GLuint index, base::span<const GLfloat> values);
+  void vertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+  void vertexAttrib4fv(GLuint index, base::span<const GLfloat> values);
+  void vertexAttribPointer(GLuint index,
+                           GLint size,
+                           GLenum type,
+                           GLboolean normalized,
+                           GLsizei stride,
+                           int64_t offset);
+
+  void viewport(GLint x, GLint y, GLsizei width, GLsizei height);
+
+  void drawingBufferStorage(GLenum sizedformat, GLsizei width, GLsizei height);
+
+  void commit();
+
+  ScriptPromise<IDLUndefined> makeXRCompatible(ScriptState*, ExceptionState&);
+  // **************************************************************************
+  // End of WebGLRenderingContextBase's IDL methods
+  // **************************************************************************
+
+  // **************************************************************************
+  // Start of WebGL2RenderingContextBase's IDL methods
+  // **************************************************************************
+
+  /* Buffer objects */
+  void bufferData(GLenum target,
+                  MaybeShared<DOMArrayBufferView> srcData,
+                  GLenum usage,
+                  int64_t srcOffset,
+                  GLuint length);
+  void bufferSubData(GLenum target,
+                     int64_t offset,
+                     MaybeShared<DOMArrayBufferView> srcData,
+                     int64_t srcOffset,
+                     GLuint length);
+  void copyBufferSubData(GLenum readTarget,
+                         GLenum writeTarget,
+                         int64_t readOffset,
+                         int64_t writeOffset,
+                         int64_t size);
+  void getBufferSubData(GLenum target,
+                        int64_t srcByteOffset,
+                        MaybeShared<DOMArrayBufferView> dstData,
+                        int64_t dstOffset,
+                        GLuint length);
+
+  /* Framebuffer objects */
+  void blitFramebuffer(GLint src_x0,
+                       GLint src_y0,
+                       GLint src_x1,
+                       GLint src_y1,
+                       GLint dst_x0,
+                       GLint dst_y0,
+                       GLint dst_x1,
+                       GLint dst_y1,
+                       GLbitfield mask,
+                       GLenum filter);
+  void framebufferTextureLayer(GLenum target,
+                               GLenum attachment,
+                               WebGLTexture* texture,
+                               GLint level,
+                               GLint layer);
+  ScriptValue getInternalformatParameter(ScriptState* script_state,
+                                         GLenum target,
+                                         GLenum internalformat,
+                                         GLenum pname);
+  void invalidateFramebuffer(GLenum target, const Vector<GLenum>& attachments);
+  void invalidateSubFramebuffer(GLenum target,
+                                const Vector<GLenum>& attachments,
+                                GLint x,
+                                GLint y,
+                                GLsizei width,
+                                GLsizei height);
+  void readBuffer(GLenum mode);
+
+  /* Renderbuffer objects */
+  void renderbufferStorageMultisample(GLenum target,
+                                      GLsizei samples,
+                                      GLenum internalformat,
+                                      GLsizei width,
+                                      GLsizei height);
+
+  /* Texture objects */
+  void texImage2D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  int64_t offset);
+  void texImage2D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  ImageData* pixels);
+  void texImage2D(ScriptState* script_state,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  HTMLImageElement* image,
+                  ExceptionState& exception_state);
+  // Handles both OffscreenCanvas and HTMLCanvasElement
+  void texImage2D(ScriptState* script_state,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  CanvasRenderingContextHost* canvas,
+                  ExceptionState& exception_state);
+  void texImage2D(ScriptState* script_state,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  HTMLVideoElement* video,
+                  ExceptionState& exception_state);
+  void texImage2D(ScriptState* script_state,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  VideoFrame* frame,
+                  ExceptionState& exception_state);
+  void texImage2D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  ImageBitmap* bitmap,
+                  ExceptionState& exception_state);
+  void texImage2D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  MaybeShared<DOMArrayBufferView> data,
+                  int64_t src_offset);
+
+  void texElement2D(ScriptState* script_state,
+                    GLenum target,
+                    GLint level,
+                    GLint internalformat,
+                    GLenum format,
+                    GLenum type,
+                    Element* element,
+                    ExceptionState& exception_state);
+
+  void texSubImage2D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     int64_t offset);
+  void texSubImage2D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     ImageData* pixels);
+  void texSubImage2D(ScriptState* script_state,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     HTMLImageElement* image,
+                     ExceptionState& exception_state);
+  // Handles both OffscreenCanvas and HTMLCanvasElement
+  void texSubImage2D(ScriptState* script_state,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     CanvasRenderingContextHost* canvas,
+                     ExceptionState& exception_state);
+  void texSubImage2D(ScriptState* script_state,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     HTMLVideoElement* video,
+                     ExceptionState& exception_state);
+  void texSubImage2D(ScriptState* script_state,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     VideoFrame* frame,
+                     ExceptionState& exception_state);
+  void texSubImage2D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     ImageBitmap* bitmap,
+                     ExceptionState& exception_state);
+  void texSubImage2D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLenum format,
+                     GLenum type,
+                     MaybeShared<DOMArrayBufferView> pixels,
+                     int64_t src_offset);
+
+  void texStorage2D(GLenum target,
+                    GLsizei levels,
+                    GLenum internalformat,
+                    GLsizei width,
+                    GLsizei height);
+  void texStorage3D(GLenum target,
+                    GLsizei levels,
+                    GLenum internalformat,
+                    GLsizei width,
+                    GLsizei height,
+                    GLsizei depth);
+  void texImage3D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLsizei depth,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  int64_t offset);
+  void texImage3D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLsizei depth,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  ImageData* pixels);
+  void texImage3D(ScriptState* script_state,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLsizei depth,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  HTMLImageElement* image,
+                  ExceptionState& exception_state);
+  // Handles both OffscreenCanvas and HTMLCanvasElement
+  void texImage3D(ScriptState* script_state,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLsizei depth,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  CanvasRenderingContextHost* canvas,
+                  ExceptionState& exception_state);
+  void texImage3D(ScriptState* script_state,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLsizei depth,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  HTMLVideoElement* video,
+                  ExceptionState& exception_state);
+  void texImage3D(ScriptState* script_state,
+                  GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLsizei depth,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  VideoFrame* frame,
+                  ExceptionState& exception_state);
+  void texImage3D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLsizei depth,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  ImageBitmap* bitmap,
+                  ExceptionState& exception_state);
+  void texImage3D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLsizei depth,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  MaybeShared<DOMArrayBufferView> pixels);
+  void texImage3D(GLenum target,
+                  GLint level,
+                  GLint internalformat,
+                  GLsizei width,
+                  GLsizei height,
+                  GLsizei depth,
+                  GLint border,
+                  GLenum format,
+                  GLenum type,
+                  MaybeShared<DOMArrayBufferView> pixels,
+                  GLuint src_offset);
+  void texSubImage3D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLint zoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLsizei depth,
+                     GLenum format,
+                     GLenum type,
+                     int64_t offset);
+  void texSubImage3D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLint zoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLsizei depth,
+                     GLenum format,
+                     GLenum type,
+                     ImageData* pixels);
+  void texSubImage3D(ScriptState* script_state,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLint zoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLsizei depth,
+                     GLenum format,
+                     GLenum type,
+                     HTMLImageElement* image,
+                     ExceptionState& exception_state);
+  // Handles both OffscreenCanvas and HTMLCanvasElement
+  void texSubImage3D(ScriptState* script_state,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLint zoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLsizei depth,
+                     GLenum format,
+                     GLenum type,
+                     CanvasRenderingContextHost* context_host,
+                     ExceptionState& exception_state);
+  void texSubImage3D(ScriptState* script_state,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLint zoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLsizei depth,
+                     GLenum format,
+                     GLenum type,
+                     HTMLVideoElement* video,
+                     ExceptionState& exception_state);
+  void texSubImage3D(ScriptState* script_state,
+                     GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLint zoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLsizei depth,
+                     GLenum format,
+                     GLenum type,
+                     VideoFrame* frame,
+                     ExceptionState& exception_state);
+  void texSubImage3D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLint zoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLsizei depth,
+                     GLenum format,
+                     GLenum type,
+                     ImageBitmap* bitmap,
+                     ExceptionState& exception_state);
+  void texSubImage3D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLint zoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLsizei depth,
+                     GLenum format,
+                     GLenum type,
+                     MaybeShared<DOMArrayBufferView> pixels);
+  void texSubImage3D(GLenum target,
+                     GLint level,
+                     GLint xoffset,
+                     GLint yoffset,
+                     GLint zoffset,
+                     GLsizei width,
+                     GLsizei height,
+                     GLsizei depth,
+                     GLenum format,
+                     GLenum type,
+                     MaybeShared<DOMArrayBufferView> pixels,
+                     GLuint src_offset);
+
+  void copyTexSubImage3D(GLenum target,
+                         GLint level,
+                         GLint xoffset,
+                         GLint yoffset,
+                         GLint zoffset,
+                         GLint x,
+                         GLint y,
+                         GLsizei width,
+                         GLsizei height);
+
+  void compressedTexImage2D(GLenum target,
+                            GLint level,
+                            GLenum internalformat,
+                            GLsizei width,
+                            GLsizei height,
+                            GLint border,
+                            MaybeShared<DOMArrayBufferView> data,
+                            GLuint src_offset,
+                            GLuint src_length_override);
+  void compressedTexSubImage2D(GLenum target,
+                               GLint level,
+                               GLint xoffset,
+                               GLint yoffset,
+                               GLsizei width,
+                               GLsizei height,
+                               GLenum format,
+                               MaybeShared<DOMArrayBufferView> data,
+                               GLuint src_offset,
+                               GLuint src_length_override);
+  void compressedTexImage3D(GLenum target,
+                            GLint level,
+                            GLenum internalformat,
+                            GLsizei width,
+                            GLsizei height,
+                            GLsizei depth,
+                            GLint border,
+                            MaybeShared<DOMArrayBufferView> data,
+                            GLuint src_offset,
+                            GLuint src_length_override);
+  void compressedTexSubImage3D(GLenum target,
+                               GLint level,
+                               GLint xoffset,
+                               GLint yoffset,
+                               GLint zoffset,
+                               GLsizei width,
+                               GLsizei height,
+                               GLsizei depth,
+                               GLenum format,
+                               MaybeShared<DOMArrayBufferView> data,
+                               GLuint src_offset,
+                               GLuint src_length_override);
+  void compressedTexImage2D(GLenum target,
+                            GLint level,
+                            GLenum internalformat,
+                            GLsizei width,
+                            GLsizei height,
+                            GLint border,
+                            GLsizei image_size,
+                            int64_t offset);
+  void compressedTexSubImage2D(GLenum target,
+                               GLint level,
+                               GLint xoffset,
+                               GLint yoffset,
+                               GLsizei width,
+                               GLsizei height,
+                               GLenum format,
+                               GLsizei image_size,
+                               int64_t offset);
+  void compressedTexImage3D(GLenum target,
+                            GLint level,
+                            GLenum internalformat,
+                            GLsizei width,
+                            GLsizei height,
+                            GLsizei depth,
+                            GLint border,
+                            GLsizei image_size,
+                            int64_t offset);
+  void compressedTexSubImage3D(GLenum target,
+                               GLint level,
+                               GLint xoffset,
+                               GLint yoffset,
+                               GLint zoffset,
+                               GLsizei width,
+                               GLsizei height,
+                               GLsizei depth,
+                               GLenum format,
+                               GLsizei image_size,
+                               int64_t offset);
+
+  /* Programs and shaders */
+  GLint getFragDataLocation(WebGLProgram* program, const String& name);
+
+  /* Uniforms and attributes */
+  void uniform1ui(const WebGLUniformLocation* location, GLuint v0);
+  void uniform2ui(const WebGLUniformLocation* location, GLuint v0, GLuint v1);
+  void uniform3ui(const WebGLUniformLocation* location,
+                  GLuint v0,
+                  GLuint v1,
+                  GLuint v2);
+  void uniform4ui(const WebGLUniformLocation* location,
+                  GLuint v0,
+                  GLuint v1,
+                  GLuint v2,
+                  GLuint v3);
+  void uniform1fv(const WebGLUniformLocation* location,
+                  base::span<const GLfloat> v,
+                  GLuint src_offset,
+                  GLuint src_length);
+  void uniform2fv(const WebGLUniformLocation* location,
+                  base::span<const GLfloat> v,
+                  GLuint src_offset,
+                  GLuint src_length);
+  void uniform3fv(const WebGLUniformLocation* location,
+                  base::span<const GLfloat> v,
+                  GLuint src_offset,
+                  GLuint src_length);
+  void uniform4fv(const WebGLUniformLocation* location,
+                  base::span<const GLfloat> v,
+                  GLuint src_offset,
+                  GLuint src_length);
+  void uniform1iv(const WebGLUniformLocation* location,
+                  base::span<const GLint> v,
+                  GLuint src_offset,
+                  GLuint src_length);
+  void uniform2iv(const WebGLUniformLocation* location,
+                  base::span<const GLint> v,
+                  GLuint src_offset,
+                  GLuint src_length);
+  void uniform3iv(const WebGLUniformLocation* location,
+                  base::span<const GLint> v,
+                  GLuint src_offset,
+                  GLuint src_length);
+  void uniform4iv(const WebGLUniformLocation* location,
+                  base::span<const GLint> v,
+                  GLuint src_offset,
+                  GLuint src_length);
+  void uniform1uiv(const WebGLUniformLocation* location,
+                   base::span<const GLuint> v,
+                   GLuint src_offset,
+                   GLuint src_length);
+  void uniform2uiv(const WebGLUniformLocation* location,
+                   base::span<const GLuint> v,
+                   GLuint src_offset,
+                   GLuint src_length);
+  void uniform3uiv(const WebGLUniformLocation* location,
+                   base::span<const GLuint> v,
+                   GLuint src_offset,
+                   GLuint src_length);
+  void uniform4uiv(const WebGLUniformLocation* location,
+                   base::span<const GLuint> v,
+                   GLuint src_offset,
+                   GLuint src_length);
+  void uniformMatrix2fv(const WebGLUniformLocation* location,
+                        GLboolean transpose,
+                        base::span<const GLfloat> v,
+                        GLuint src_offset,
+                        GLuint src_length);
+  void uniformMatrix3fv(const WebGLUniformLocation* location,
+                        GLboolean transpose,
+                        base::span<const GLfloat> v,
+                        GLuint src_offset,
+                        GLuint src_length);
+  void uniformMatrix4fv(const WebGLUniformLocation* location,
+                        GLboolean transpose,
+                        base::span<const GLfloat> v,
+                        GLuint src_offset,
+                        GLuint src_length);
+  void uniformMatrix2x3fv(const WebGLUniformLocation* location,
+                          GLboolean transpose,
+                          base::span<const GLfloat> v,
+                          GLuint src_offset,
+                          GLuint src_length);
+  void uniformMatrix3x2fv(const WebGLUniformLocation* location,
+                          GLboolean transpose,
+                          base::span<const GLfloat> v,
+                          GLuint src_offset,
+                          GLuint src_length);
+  void uniformMatrix2x4fv(const WebGLUniformLocation* location,
+                          GLboolean transpose,
+                          base::span<const GLfloat> v,
+                          GLuint src_offset,
+                          GLuint src_length);
+  void uniformMatrix4x2fv(const WebGLUniformLocation* location,
+                          GLboolean transpose,
+                          base::span<const GLfloat> v,
+                          GLuint src_offset,
+                          GLuint src_length);
+  void uniformMatrix3x4fv(const WebGLUniformLocation* location,
+                          GLboolean transpose,
+                          base::span<const GLfloat> v,
+                          GLuint src_offset,
+                          GLuint src_length);
+  void uniformMatrix4x3fv(const WebGLUniformLocation* location,
+                          GLboolean transpose,
+                          base::span<const GLfloat> v,
+                          GLuint src_offset,
+                          GLuint src_length);
+
+  void vertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w);
+  void vertexAttribI4iv(GLuint index, base::span<const GLint> v);
+  void vertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
+  void vertexAttribI4uiv(GLuint index, base::span<const GLuint> v);
+  void vertexAttribIPointer(GLuint index,
+                            GLint size,
+                            GLenum type,
+                            GLsizei stride,
+                            int64_t offset);
+
+  /* Writing to the drawing buffer */
+  void vertexAttribDivisor(GLuint index, GLuint divisor);
+  void drawArraysInstanced(GLenum mode,
+                           GLint first,
+                           GLsizei count,
+                           GLsizei instance_count);
+  void drawElementsInstanced(GLenum mode,
+                             GLsizei count,
+                             GLenum type,
+                             int64_t offset,
+                             GLsizei instance_count);
+  void drawRangeElements(GLenum mode,
+                         GLuint start,
+                         GLuint end,
+                         GLsizei count,
+                         GLenum type,
+                         int64_t offset);
+
+  /* Multiple Render Targets */
+  void drawBuffers(const Vector<GLenum>& buffers);
+  void clearBufferiv(GLenum buffer,
+                     GLint drawbuffer,
+                     base::span<const GLint> value,
+                     GLuint src_offset);
+  void clearBufferuiv(GLenum buffer,
+                      GLint drawbuffer,
+                      base::span<const GLuint> value,
+                      GLuint src_offset);
+  void clearBufferfv(GLenum buffer,
+                     GLint drawbuffer,
+                     base::span<const GLfloat> value,
+                     GLuint src_offset);
+  void clearBufferfi(GLenum buffer,
+                     GLint drawbuffer,
+                     GLfloat depth,
+                     GLint stencil);
+
+  /* Query Objects */
+  WebGLQuery* createQuery();
+  void deleteQuery(WebGLQuery* query);
+  bool isQuery(WebGLQuery* query);
+  void beginQuery(GLenum target, WebGLQuery* query);
+  void endQuery(GLenum target);
+  ScriptValue getQuery(ScriptState* script_state, GLenum target, GLenum pname);
+  ScriptValue getQueryParameter(ScriptState* script_state,
+                                WebGLQuery* query,
+                                GLenum pname);
+
+  /* Sampler Objects */
+  WebGLSampler* createSampler();
+  void deleteSampler(WebGLSampler* sampler);
+  bool isSampler(WebGLSampler* sampler);
+  void bindSampler(GLuint unit, WebGLSampler* sampler);
+  void samplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param);
+  void samplerParameterf(WebGLSampler* sampler, GLenum pname, GLfloat param);
+  ScriptValue getSamplerParameter(ScriptState* script_state,
+                                  WebGLSampler* sampler,
+                                  GLenum pname);
+
+  /* Sync objects */
+  WebGLSync* fenceSync(GLenum condition, GLbitfield flags);
+  bool isSync(WebGLSync* sync);
+  void deleteSync(WebGLSync* sync);
+  GLenum clientWaitSync(WebGLSync* sync, GLbitfield flags, GLuint64 timeout);
+  void waitSync(WebGLSync* sync, GLbitfield flags, GLint64 timeout);
+
+  ScriptValue getSyncParameter(ScriptState* script_state,
+                               WebGLSync* sync,
+                               GLenum pname);
+
+  /* Transform Feedback */
+  WebGLTransformFeedback* createTransformFeedback();
+  void deleteTransformFeedback(WebGLTransformFeedback* feedback);
+  bool isTransformFeedback(WebGLTransformFeedback* feedback);
+  void bindTransformFeedback(GLenum target, WebGLTransformFeedback* feedback);
+  void beginTransformFeedback(GLenum primitive_mode);
+  void endTransformFeedback();
+  void transformFeedbackVaryings(WebGLProgram* program,
+                                 const Vector<String>& varyings,
+                                 GLenum buffer_mode);
+  WebGLActiveInfo* getTransformFeedbackVarying(WebGLProgram* program,
+                                               GLuint index);
+  void pauseTransformFeedback();
+  void resumeTransformFeedback();
+
+  /* Uniform Buffer Objects and Transform Feedback Buffers */
+  void bindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer);
+  void bindBufferRange(GLenum target,
+                       GLuint index,
+                       WebGLBuffer* buffer,
+                       int64_t offset,
+                       int64_t size);
+  ScriptValue getIndexedParameter(ScriptState* script_state,
+                                  GLenum target,
+                                  GLuint index);
+  std::optional<Vector<GLuint>> getUniformIndices(
+      WebGLProgram* program,
+      const Vector<String>& uniform_names);
+  ScriptValue getActiveUniforms(ScriptState* script_state,
+                                WebGLProgram* program,
+                                const Vector<GLuint>& uniform_indices,
+                                GLenum pname);
+  GLuint getUniformBlockIndex(WebGLProgram* program,
+                              const String& uniform_block_name);
+  ScriptValue getActiveUniformBlockParameter(ScriptState* script_state,
+                                             WebGLProgram* program,
+                                             GLuint uniform_block_index,
+                                             GLenum pname);
+  String getActiveUniformBlockName(WebGLProgram* program,
+                                   GLuint uniform_block_index);
+  void uniformBlockBinding(WebGLProgram* program,
+                           GLuint uniform_block_index,
+                           GLuint uniform_block_binding);
+
+  /* Vertex Array Objects */
+  WebGLVertexArrayObject* createVertexArray();
+  void deleteVertexArray(WebGLVertexArrayObject* vertex_array);
+  bool isVertexArray(WebGLVertexArrayObject* vertex_array);
+  void bindVertexArray(WebGLVertexArrayObject* vertex_array);
+
+  /* Reading */
+  void readPixels(GLint x,
+                  GLint y,
+                  GLsizei width,
+                  GLsizei height,
+                  GLenum format,
+                  GLenum type,
+                  int64_t offset);
+  void readPixels(GLint x,
+                  GLint y,
+                  GLsizei width,
+                  GLsizei height,
+                  GLenum format,
+                  GLenum type,
+                  MaybeShared<DOMArrayBufferView> pixels,
+                  int64_t offset);
+
+  // **************************************************************************
+  // End of WebGL2RenderingContextBase's IDL methods
+  // **************************************************************************
+
+  // **************************************************************************
+  // Start of CanvasRenderingContext implementation
+  // **************************************************************************
+  SkAlphaType GetAlphaType() const override;
+  viz::SharedImageFormat GetSharedImageFormat() const override;
+  gfx::ColorSpace GetColorSpace() const override;
+  bool isContextLost() const override;
+  scoped_refptr<StaticBitmapImage> GetImage(FlushReason) override;
+  void SetHdrMetadata(const gfx::HDRMetadata& hdr_metadata) override;
+
+  bool IsComposited() const override;
+  bool IsPaintable() const override;
+  bool UsingSwapChain() const override;
+  void PageVisibilityChanged() override;
+  bool PaintRenderingResultsToCanvas(SourceDrawingBuffer) override;
+  bool CopyRenderingResultsToVideoFrame(
+      WebGraphicsContext3DVideoFramePool*,
+      SourceDrawingBuffer,
+      const gfx::ColorSpace&,
+      VideoFrameCopyCompletedCallback) override;
+
+  cc::Layer* CcLayer() const override;
+  void Stop() override;
+  void FinalizeFrame(FlushReason) override;
+  bool PushFrame() override;
+
+  // **************************************************************************
+  // End of CanvasRenderingContext implementation
+  // **************************************************************************
+
+  void Trace(Visitor*) const override;
+
+ private:
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_RENDERING_CONTEXT_WEBGPU_BASE_H_
diff --git a/third_party/blink/renderer/modules/webgpu/BUILD.gn b/third_party/blink/renderer/modules/webgpu/BUILD.gn
index d429879..95caaa2 100644
--- a/third_party/blink/renderer/modules/webgpu/BUILD.gn
+++ b/third_party/blink/renderer/modules/webgpu/BUILD.gn
@@ -110,7 +110,6 @@
     "//services/metrics/public/cpp:ukm_builders",
     "//skia",
     "//skia:skcms",
-    "//third_party/blink/renderer/modules/canvas:canvas_proxy_headers",
     "//third_party/blink/renderer/modules/webcodecs:webcodecs",
   ]
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
index 803ae29..aee809e81 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
@@ -12,12 +12,12 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_configuration.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_tone_mapping.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_tone_mapping_mode.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_offscreen_rendering_context.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_rendering_context.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/canvas/predefined_color_space.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
 #include "third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h"
-#include "third_party/blink/renderer/modules/canvas/htmlcanvas/v8_rendering_context.h"
-#include "third_party/blink/renderer/modules/canvas/offscreencanvas/v8_offscreen_rendering_context.h"
 #include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h"
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
index b41d38d3..288a80b 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -38,31 +38,32 @@
 
 namespace blink {
 
-struct CanvasResourceDispatcher::FrameResource {
+// Holds the ref and release callback for a CanvasResource that has been
+// exported to the compositor, to be released when either (a) the compositor
+// notifies CanvasResourceDispatcher that it no longer requires this resource,
+// or (b) the CanvasResourceDispatcher is torn down (e.g., because its owning
+// thread was torn down).
+struct CanvasResourceDispatcher::ExportedResource {
  public:
-  FrameResource(scoped_refptr<CanvasResource> resource,
-                CanvasResource::ReleaseCallback callback)
-      : canvas_resource_(std::move(resource)),
-        release_callback_(std::move(callback)) {
-    CHECK(canvas_resource_);
+  ExportedResource(scoped_refptr<CanvasResource> resource,
+                   CanvasResource::ReleaseCallback callback)
+      : resource_(std::move(resource)), release_callback_(std::move(callback)) {
+    CHECK(resource_);
   }
-  ~FrameResource() {
+
+  void ReleaseResource(const gpu::SyncToken& sync_token, bool is_lost) {
+    auto resource = std::move(resource_);
     if (release_callback_) {
       std::move(release_callback_)
-          .Run(std::move(canvas_resource_), sync_token_, is_lost_);
+          .Run(std::move(resource), sync_token, is_lost);
     }
   }
 
-  void set_sync_token(const gpu::SyncToken& sync_token) {
-    sync_token_ = sync_token;
-  }
-  void set_is_lost(bool is_lost) { is_lost_ = is_lost; }
+  ~ExportedResource() { ReleaseResource(gpu::SyncToken(), /*is_lost=*/false); }
 
  private:
-  scoped_refptr<CanvasResource> canvas_resource_;
+  scoped_refptr<CanvasResource> resource_;
   CanvasResource::ReleaseCallback release_callback_;
-  gpu::SyncToken sync_token_;
-  bool is_lost_ = false;
 };
 
 CanvasResourceDispatcher::CanvasResourceDispatcher(
@@ -309,9 +310,9 @@
   // duration of the compositor's usage (we'll drop our ref when the compositor
   // notifies us that it is no longer using the resource via
   // `ReclaimResources()`).
-  resources_.insert(resource_id, std::make_unique<FrameResource>(
-                                     std::move(canvas_resource),
-                                     std::move(release_callback)));
+  exported_resources_.insert(resource_id, std::make_unique<ExportedResource>(
+                                              std::move(canvas_resource),
+                                              std::move(release_callback)));
 
   // TODO(crbug.com/645993): this should be inherited from WebGL context's
   // creation settings.
@@ -452,15 +453,15 @@
 void CanvasResourceDispatcher::ReclaimResources(
     WTF::Vector<viz::ReturnedResource> resources) {
   for (const auto& resource : resources) {
-    auto it = resources_.find(resource.id);
+    auto it = exported_resources_.find(resource.id);
 
-    CHECK(it != resources_.end(), base::NotFatalUntil::M130);
-    if (it == resources_.end())
+    CHECK(it != exported_resources_.end(), base::NotFatalUntil::M130);
+    if (it == exported_resources_.end()) {
       continue;
+    }
 
-    it->value->set_sync_token(resource.sync_token);
-    it->value->set_is_lost(resource.lost);
-    ReclaimResourceInternal(it);
+    it->value->ReleaseResource(resource.sync_token, resource.lost);
+    exported_resources_.erase(it);
   }
 }
 
@@ -514,9 +515,4 @@
   }
 }
 
-void CanvasResourceDispatcher::ReclaimResourceInternal(
-    const ResourceMap::iterator& it) {
-  resources_.erase(it);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
index 29e2a43..ecfd3d2 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
@@ -125,9 +125,10 @@
  private:
   friend class OffscreenCanvasPlaceholderTest;
   friend class CanvasResourceDispatcherTest;
-  struct FrameResource;
+  struct ExportedResource;
 
-  using ResourceMap = HashMap<viz::ResourceId, std::unique_ptr<FrameResource>>;
+  using ExportedResourceMap =
+      HashMap<viz::ResourceId, std::unique_ptr<ExportedResource>>;
 
   bool PrepareFrame(scoped_refptr<CanvasResource>&&,
                     base::TimeTicks commit_start_time,
@@ -161,8 +162,6 @@
   virtual void PostImageToPlaceholder(scoped_refptr<CanvasResource>&&,
                                       viz::ResourceId resource_id);
 
-  void ReclaimResourceInternal(const ResourceMap::iterator&);
-
   mojo::Remote<viz::mojom::blink::CompositorFrameSink> sink_;
   mojo::Remote<mojom::blink::SurfaceEmbedder> surface_embedder_;
   mojo::Receiver<viz::mojom::blink::CompositorFrameSinkClient> receiver_{this};
@@ -170,7 +169,11 @@
   int placeholder_canvas_id_;
 
   viz::ResourceIdGenerator id_generator_;
-  ResourceMap resources_;
+
+  // Stores resources that have been exported to the compositor, to be released
+  // when the compositor no longer requires them (or in the limit when this
+  // instance is destroyed).
+  ExportedResourceMap exported_resources_;
 
   viz::FrameTokenGenerator next_frame_token_;
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index b334ca9..9cb98dd 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2614,6 +2614,10 @@
       name: "HTMLParserYieldAndDelayOftenForTesting",
     },
     {
+      name: "HTMLParserYieldByUserTiming",
+      status: "test",
+    },
+    {
       name: "HTMLSelectElementShowPicker",
       status: "stable",
     },
diff --git a/third_party/blink/renderer/platform/text/character.h b/third_party/blink/renderer/platform/text/character.h
index 240a825a..b74d6f1 100644
--- a/third_party/blink/renderer/platform/text/character.h
+++ b/third_party/blink/renderer/platform/text/character.h
@@ -145,6 +145,7 @@
     return IsInRange(character, kLeftSingleQuotationMarkCharacter, 0x301F) ||
            IsInRange(character, 0xFF08, 0xFF60);
   }
+  static bool MayNeedEastAsianSpacing(UChar32);
 
   // Collapsible white space characters defined in CSS:
   // https://drafts.csswg.org/css-text-3/#collapsible-white-space
@@ -317,6 +318,14 @@
           EastAsianWidth(ch) == UEastAsianWidth::U_EA_FULLWIDTH);
 }
 
+inline bool Character::MayNeedEastAsianSpacing(UChar32 ch) {
+  // `EastAsianSpacingType::kWide` may need the spacing.
+  // U+2000-206F General Punctuation has rather popular characters, such as ZWSP
+  // and curly quotation marks. Exclude the largest range of non-`kWide` that
+  // include them.
+  return ch >= 0x02C7 && !IsInRange(ch, 0x1200, 0x3004);
+}
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CHARACTER_H_
diff --git a/third_party/blink/renderer/platform/text/character_property_data_generator.cc b/third_party/blink/renderer/platform/text/character_property_data_generator.cc
index ec4b09cb..cf777cd 100644
--- a/third_party/blink/renderer/platform/text/character_property_data_generator.cc
+++ b/third_party/blink/renderer/platform/text/character_property_data_generator.cc
@@ -500,8 +500,10 @@
             "\"third_party/blink/renderer/platform/wtf/text/wtf_uchar.h\"\n"
             "\nnamespace {\n\n");
 
-    fprintf(fp, "constexpr UChar kFastLineBreakMinChar = 0x%02X;\n", kMinChar);
-    fprintf(fp, "constexpr UChar kFastLineBreakMaxChar = 0x%02X;\n", kMaxChar);
+    fprintf(fp, "inline constexpr UChar kFastLineBreakMinChar = 0x%02X;\n",
+            kMinChar);
+    fprintf(fp, "inline constexpr UChar kFastLineBreakMaxChar = 0x%02X;\n",
+            kMaxChar);
 
     // Define macros.
     fprintf(fp,
diff --git a/third_party/blink/renderer/platform/text/character_test.cc b/third_party/blink/renderer/platform/text/character_test.cc
index 620d872..11e705c 100644
--- a/third_party/blink/renderer/platform/text/character_test.cc
+++ b/third_party/blink/renderer/platform/text/character_test.cc
@@ -50,6 +50,11 @@
     EXPECT_EQ(Character::IsEastAsianWidthFullwidth(ch),
               eaw == UEastAsianWidth::U_EA_FULLWIDTH);
 
+    if (!Character::MayNeedEastAsianSpacing(ch)) {
+      EastAsianSpacingType type = Character::GetEastAsianSpacingType(ch);
+      DCHECK_NE(type, EastAsianSpacingType::kWide);
+    }
+
     if (!Character::MaybeHanKerningOpenOrCloseFast(ch)) {
       DCHECK(!Character::MaybeHanKerningOpenSlow(ch));
       DCHECK(!Character::MaybeHanKerningCloseSlow(ch));
diff --git a/third_party/blink/renderer/platform/wtf/text/strcat.cc b/third_party/blink/renderer/platform/wtf/text/strcat.cc
index bfc1006b..edfc2d0d6 100644
--- a/third_party/blink/renderer/platform/wtf/text/strcat.cc
+++ b/third_party/blink/renderer/platform/wtf/text/strcat.cc
@@ -11,14 +11,24 @@
   bool is_8bit = true;
   for (const auto& view : pieces) {
     size += view.length();
-    is_8bit = is_8bit && view.Is8Bit();
+    if (is_8bit && !view.Is8Bit()) {
+      // Like StringBuilder, we check one-length 16bit strings.
+      is_8bit = view.length() == 1 && view[0] < 0x0100;
+    }
   }
 
   if (is_8bit) {
     base::span<LChar> buffer;
     auto impl = StringImpl::CreateUninitialized(size, buffer);
     for (const auto& view : pieces) {
-      buffer.take_first(view.length()).copy_from(view.Span8());
+      base::span<LChar> sub_buffer = buffer.take_first(view.length());
+      if (view.Is8Bit()) {
+        sub_buffer.copy_from(view.Span8());
+      } else {
+        DCHECK_EQ(sub_buffer.size(), 1u);
+        DCHECK_LT(view[0], 0x0100);
+        sub_buffer[0] = view[0];
+      }
     }
     return impl;
   }
diff --git a/third_party/blink/renderer/platform/wtf/text/strcat_test.cc b/third_party/blink/renderer/platform/wtf/text/strcat_test.cc
index 6eddef1..1fcb6f4 100644
--- a/third_party/blink/renderer/platform/wtf/text/strcat_test.cc
+++ b/third_party/blink/renderer/platform/wtf/text/strcat_test.cc
@@ -31,4 +31,12 @@
   EXPECT_FALSE(result.Is8Bit());
 }
 
+TEST(StrCatTest, MixedBitsResulting8it) {
+  const String src16(u"a");
+  ASSERT_FALSE(src16.Is8Bit());
+  String result = StrCat({"foo \"", src16, "\" bar."});
+  EXPECT_EQ("foo \"a\" bar.", result);
+  EXPECT_TRUE(result.Is8Bit());
+}
+
 }  // namespace WTF
diff --git a/third_party/blink/tools/blinkpy/common/config/builders.json b/third_party/blink/tools/blinkpy/common/config/builders.json
index 95a83a55e5..f689298 100644
--- a/third_party/blink/tools/blinkpy/common/config/builders.json
+++ b/third_party/blink/tools/blinkpy/common/config/builders.json
@@ -325,14 +325,5 @@
             "android_chrome_wpt_tests": {}
         },
         "is_try_builder": true
-    },
-    "android-webview-13-x64-wpt-android-specific": {
-        "main": "tryserver.chromium.android",
-        "port_name": "webview",
-        "specifiers": ["Webview", "Release"],
-        "steps": {
-            "android_webview_wpt_tests": {}
-        },
-        "is_try_builder": true
     }
 }
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 8807619..0a7e2925 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -2566,6 +2566,16 @@
             'network::PermissionsPolicy',
         ]
     },
+    {
+        # TODO(crbug.com/418169222): Remove this entry once the device
+        # bound session credentials origin trial is complete.
+        'paths': [
+            'third_party/blink/common/loader/throttling_url_loader.cc',
+        ],
+        'allowed': [
+            'mojom::OriginTrialFeature',
+        ]
+    }
 ]
 
 
diff --git a/third_party/blink/tools/blinkpy/tool/commands/analyze_baselines.py b/third_party/blink/tools/blinkpy/tool/commands/analyze_baselines.py
index a9a0619c..bf91606 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/analyze_baselines.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/analyze_baselines.py
@@ -80,7 +80,7 @@
 
     def execute(self, options, args, tool):
         self._tool = tool
-        self._baseline_suffix_list = options.suffixes.split(',')
+        self._baseline_suffix_list = options.suffixes
         port_names = tool.port_factory.all_port_names(options.platform)
         if not port_names:
             _log.error("No port names match '%s'", options.platform)
diff --git a/third_party/blink/tools/blinkpy/tool/commands/analyze_baselines_unittest.py b/third_party/blink/tools/blinkpy/tool/commands/analyze_baselines_unittest.py
index 10b07c03..38144976 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/analyze_baselines_unittest.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/analyze_baselines_unittest.py
@@ -31,7 +31,7 @@
     def test_default(self):
         self.command.execute(
             optparse.Values(
-                dict(suffixes='txt', missing=False, platform=None)),
+                dict(suffixes=['txt'], missing=False, platform=None)),
             ['passes/text.html'], self.tool)
         self.assertEqual(self.lines,
                          ['passes/text-expected.txt:', '  (generic): 123456'])
@@ -39,7 +39,7 @@
     def test_missing_baselines(self):
         self.command.execute(
             optparse.Values(
-                dict(suffixes='png,txt', missing=True, platform=None)),
+                dict(suffixes=['png', 'txt'], missing=True, platform=None)),
             ['passes/text.html'], self.tool)
         self.assertEqual(self.lines, [
             'passes/text-expected.png: (no baselines found)',
diff --git a/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines.py b/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines.py
index 0615368..2342293a 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines.py
@@ -3,20 +3,15 @@
 # found in the LICENSE file.
 
 import functools
-import itertools
 import logging
 import optparse
-from typing import Collection, List, Set, Tuple
 
 from blinkpy.common.checkout.baseline_optimizer import BaselineOptimizer
-from blinkpy.common.net.web_test_results import BaselineSuffix
 from blinkpy.tool.commands.command import resolve_test_patterns
 from blinkpy.tool.commands.rebaseline import AbstractParallelRebaselineCommand
 
 _log = logging.getLogger(__name__)
 
-OptimizationTask = Tuple[str, str, BaselineSuffix]
-
 
 class OptimizeBaselines(AbstractParallelRebaselineCommand):
     name = 'optimize-baselines'
@@ -71,7 +66,7 @@
         worker_factory = functools.partial(Worker,
                                            port_names=port_names,
                                            options=options)
-        tasks = self._make_tasks(test_set, options.suffixes.split(','))
+        tasks = [(self.name, test_name) for test_name in sorted(test_set)]
         self._run_in_message_pool(worker_factory, tasks)
         if options.check:
             if self._successful:
@@ -82,15 +77,6 @@
                              'to fix these issues.')
                 return 2
 
-    def _make_tasks(
-            self, test_set: Set[str],
-            suffixes: Collection[BaselineSuffix]) -> List[OptimizationTask]:
-        tasks = []
-        for test_name, suffix in itertools.product(sorted(test_set), suffixes):
-            if self._test_can_have_suffix(test_name, suffix):
-                tasks.append((self.name, test_name, suffix))
-        return tasks
-
     def _get_test_set(self, options, args):
         if options.all_tests:
             test_set = set(self._host_port.tests())
@@ -122,15 +108,19 @@
         # The manifest should already be updated by `optimize-baselines` or
         # `rebaseline-cl`.
         self._options.manifest_update = False
-        self._optimizer = BaselineOptimizer(
-            self._connection.host,
-            self._connection.host.port_factory.get(options=self._options),
-            self._port_names,
-            check=self._options.check)
+        self._default_port = self._connection.host.port_factory.get(
+            options=self._options)
+        self._optimizer = BaselineOptimizer(self._connection.host,
+                                            self._default_port,
+                                            self._port_names,
+                                            check=self._options.check)
 
-    def handle(self, name: str, source: str, test_name: str,
-               suffix: BaselineSuffix):
-        successful = self._optimizer.optimize(test_name, suffix)
+    def handle(self, name: str, source: str, test_name: str):
+        suffixes = sorted(
+            set(self._options.suffixes)
+            & self._default_port.allowed_suffixes(test_name))
+        successful = all(
+            self._optimizer.optimize(test_name, suffix) for suffix in suffixes)
         if self._options.check and not self._options.verbose and successful:
             # Without `--verbose`, do not show optimization logs when a test
             # passes the check.
diff --git a/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines_unittest.py b/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines_unittest.py
index 733391a..5a23a52 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines_unittest.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/optimize_baselines_unittest.py
@@ -46,7 +46,7 @@
 
         exit_code = self.command.execute(
             optparse.Values({
-                'suffixes': 'txt,wav,png',
+                'suffixes': ['txt', 'wav', 'png'],
                 'all_tests': False,
                 'platform': 'test-mac-mac10.10',
                 'check': False,
@@ -87,7 +87,7 @@
                 """))
         exit_code = self.command.execute(
             optparse.Values({
-                'suffixes': 'txt',
+                'suffixes': ['txt'],
                 'all_tests': False,
                 'platform': None,
                 'check': False,
@@ -140,7 +140,7 @@
 
         exit_code = self.command.check_arguments_and_execute(
             optparse.Values({
-                'suffixes': 'txt,wav,png',
+                'suffixes': ['txt', 'wav', 'png'],
                 'all_tests': False,
                 'platform': 'test-mac-mac10.10',
                 'check': True,
@@ -167,7 +167,7 @@
 
         exit_code = self.command.execute(
             optparse.Values({
-                'suffixes': 'txt',
+                'suffixes': ['txt'],
                 'all_tests': False,
                 'platform': 'test-mac-mac10.10',
                 'check': True,
@@ -195,7 +195,7 @@
 
         exit_code = self.command.execute(
             optparse.Values({
-                'suffixes': 'txt',
+                'suffixes': ['txt'],
                 'all_tests': False,
                 'platform': 'test-mac-mac10.10',
                 'check': True,
@@ -244,7 +244,7 @@
         self.command.handle = mock.Mock(wraps=self.command.handle)
         exit_code = self.command.execute(
             optparse.Values({
-                'suffixes': 'txt',
+                'suffixes': ['txt'],
                 'all_tests': False,
                 'platform': None,
                 'check': False,
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
index d8a437a..6cf078d 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
@@ -73,6 +73,14 @@
 _log = logging.getLogger(__name__)
 
 
+def parse_suffixes(option, opt_str, value, parser):
+    suffixes = set(value.split(','))
+    if invalid_suffixes := suffixes - set(get_args(BaselineSuffix)):
+        raise optparse.OptionValueError('invalid suffixes: ' +
+                                        ', '.join(sorted(invalid_suffixes)))
+    parser.values.suffixes = sorted(suffixes)
+
+
 class AbstractRebaseliningCommand(Command):
     """Base class for rebaseline-related commands."""
     # pylint: disable=abstract-method; not overriding `execute()`
@@ -104,8 +112,10 @@
         help='Local results directory to use.')
     suffixes_option = optparse.make_option(
         '--suffixes',
-        default=','.join(get_args(BaselineSuffix)),
-        action='store',
+        action='callback',
+        callback=parse_suffixes,
+        type='string',
+        default=get_args(BaselineSuffix),
         help='Comma-separated-list of file types to rebaseline.')
     builder_option = optparse.make_option(
         '--builder',
@@ -164,21 +174,6 @@
         return self._host_port.output_filename(
             test_name, test_failures.FILENAME_SUFFIX_EXPECTED, '.' + suffix)
 
-    def _test_can_have_suffix(self, test_name: str,
-                              suffix: BaselineSuffix) -> bool:
-        wpt_type = self._host_port.get_wpt_type(test_name)
-        # Only legacy reftests can dump text output, not WPT reftests.
-        if wpt_type in {'testharness', 'wdspec'} and suffix == 'txt':
-            return True
-        # Some manual tests are run as pixel tests (crbug.com/1114920), so
-        # `png` is allowed in that case.
-        elif wpt_type == 'manual' and suffix == 'png':
-            return True
-        elif self._host_port.reference_files(test_name) and suffix == 'png':
-            return False
-        # No other WPT-suffix combinations are allowed.
-        return not wpt_type
-
 
 class ChangeSet(object):
     """A record of TestExpectation lines to remove.
@@ -424,9 +419,7 @@
         for base_test in sorted(groups):
             group = groups[base_test]
             for suffix in self._suffixes_for_group(group):
-                if self._test_can_have_suffix(base_test, suffix):
-                    commands.append(
-                        ('copy_baselines', base_test, suffix, group))
+                commands.append(('copy_baselines', base_test, suffix, group))
         self._run_in_message_pool(self._worker_factory, commands)
 
     def _group_tests_by_base(
@@ -553,6 +546,8 @@
         This allows this class to conform to the `message_pool.MessageHandler`
         interface.
         """
+        if not args:
+            return
         if name == 'report_baseline_cache_stats':
             (stats, ) = args
             self.baseline_cache_stats += stats
@@ -1030,6 +1025,11 @@
 
     def _copy_baselines(self, test_name: str, suffix: BaselineSuffix,
                         group: TestBaselineSet):
+        # `suffix` is derived from test result artifacts. Check for cases where
+        # the artifact is not actually something to rebaseline (e.g., reftest
+        # PNG).
+        if suffix not in self._default_port.allowed_suffixes(test_name):
+            return
         copies = list(
             self._copier.find_baselines_to_copy(test_name, suffix, group))
         copies.sort(key=lambda copy: copy[1])
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index e8cd2c9..53887904 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -54,6 +54,7 @@
     Optional,
     Set,
     Tuple,
+    get_args,
 )
 
 import six
@@ -67,7 +68,10 @@
 from blinkpy.common import read_checksum_from_png
 from blinkpy.common.host import Host
 from blinkpy.common.memoized import memoized
-from blinkpy.common.net.web_test_results import BASELINE_EXTENSIONS
+from blinkpy.common.net.web_test_results import (
+    BaselineSuffix,
+    BASELINE_EXTENSIONS,
+)
 from blinkpy.common.system.executive import ScriptError
 from blinkpy.common.system.path import abspath_to_uri
 from blinkpy.w3c.wpt_manifest import (
@@ -829,6 +833,23 @@
 
         return baseline_dict
 
+    def allowed_suffixes(self, test_name: str) -> set[BaselineSuffix]:
+        """Get possible suffixes for the given test."""
+        wpt_type = self.get_wpt_type(test_name)
+        if wpt_type in {'testharness', 'wdspec'}:
+            return {'txt'}
+        elif wpt_type == 'manual':
+            # Some manual tests are run as pixel tests (crbug.com/1114920), so
+            # `png` is allowed in that case.
+            return {'png'}
+        elif wpt_type:
+            return set()
+
+        suffixes = set(get_args(BaselineSuffix))
+        if self.reference_files(test_name):
+            suffixes.discard('png')
+        return suffixes
+
     def output_filename(self, test_name, suffix, extension):
         """Generates the output filename for a test.
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py b/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
index 8b783aa2..3656da8 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
@@ -114,6 +114,36 @@
         port = self.make_port()
         self.assertEqual(port.get_option('foo', 'bar'), 'bar')
 
+    def test_allowed_suffixes_legacy(self):
+        port = self.make_port(with_tests=True)
+        # Depending on the `testRunner` call, any kind can be dumped.
+        self.assertEqual(port.allowed_suffixes('failures/expected/text.html'),
+                         {'txt', 'png', 'wav'})
+
+    def test_allowed_suffixes_legacy_reftest(self):
+        port = self.make_port(with_tests=True)
+        self.assertEqual(
+            port.allowed_suffixes('failures/expected/reftest.html'),
+            {'txt', 'wav'})
+
+    def test_allowed_suffixes_wpt_testharness(self):
+        port = self.make_port(with_tests=True)
+        add_manifest_to_mock_filesystem(port)
+        self.assertEqual(
+            port.allowed_suffixes(
+                'external/wpt/dom/ranges/Range-attributes.html'), {'txt'})
+
+    def test_allowed_suffixes_wpt_reftest(self):
+        port = self.make_port(with_tests=True)
+        add_manifest_to_mock_filesystem(port)
+        self.assertEqual(
+            port.allowed_suffixes('external/wpt/html/dom/elements/'
+                                  'global-attributes/dir_auto-EN-L.html'),
+            set())
+        self.assertEqual(
+            port.allowed_suffixes('external/wpt/foo/bar/test-print.html'),
+            set())
+
     def test_output_filename(self):
         port = self.make_port()
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 441b320..4035462 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -868,22 +868,6 @@
 crbug.com/357649027 external/wpt/css/css-sizing/range-percent-intrinsic-size-2.html [ Failure ]
 crbug.com/357649027 external/wpt/css/css-sizing/range-percent-intrinsic-size-2a.html [ Failure ]
 
-# New stretch behaviour in flexboxes. See:
-# https://github.com/w3c/csswg-drafts/issues/11784
-crbug.com/41253915 external/wpt/css/css-sizing/keyword-sizes-on-flex-item-001.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-alias-block-size-001.tentative.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-alias-inline-size-001.tentative.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-alias-max-block-size-001.tentative.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-alias-max-inline-size-001.tentative.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-alias-min-block-size-001.tentative.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-alias-min-inline-size-001.tentative.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-block-size-001.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-inline-size-001.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-max-block-size-001.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-max-inline-size-001.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-min-block-size-001.html [ Failure ]
-crbug.com/41253915 external/wpt/css/css-sizing/stretch/stretch-min-inline-size-001.html [ Failure ]
-
 ##### This one passes only when it includes an iframe. Removing the iframe makes the test fail.
 crbug.com/327723279 external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-033.html [ Failure ]
 
diff --git a/third_party/blink/web_tests/WebGPUExpectations b/third_party/blink/web_tests/WebGPUExpectations
index e959d67..5ef120e 100644
--- a/third_party/blink/web_tests/WebGPUExpectations
+++ b/third_party/blink/web_tests/WebGPUExpectations
@@ -50,3 +50,7 @@
 
 # Test failus due to drop last frame after gpu device is destroyed/lost.
 crbug.com/370694819 wpt_internal/webgpu/web_platform/reftests/canvas_display_after_device_lost.https.html [ Failure ]
+
+# Crashes due to the upgrade from Win10/old driver -> Win11/new driver for NVIDIA GTX 1660.
+crbug.com/418169213 [ Win11 ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_rgba16float_store.https.html [ Crash ]
+crbug.com/418169213 [ Win11 ] wpt_internal/webgpu/web_platform/reftests/canvas_complex_rgba8unorm_store.https.html [ Crash ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 6bd99a4..aa23da3e 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -410231,6 +410231,10 @@
      "122d12ba1cc1a1bb44a71d29b9111269842cb92c",
      []
     ],
+    "roles-minimum.tentative-expected.txt": [
+     "24cbf65c0fae051c583c03d2e47c522b01bac077",
+     []
+    ],
     "roles.tentative-expected.txt": [
      "94c90f2226600f311a9fa62ccc86b26684380bb1",
      []
@@ -426404,14 +426408,6 @@
       "a572f55e66dbce825d399f61c4a2dfa34df9b4ca",
       []
      ]
-    },
-    "smoke": {
-     "tentative": {
-      "almost-soft-navigation-expected.txt": [
-       "36456e16370b5ac50a176307e7e831a27135fe84",
-       []
-      ]
-     }
     }
    },
    "speculation-rules": {
@@ -520389,7 +520385,7 @@
       ]
      ],
      "keyword-sizes-on-flex-item-001.html": [
-      "04c4bddbbd19c3a2942e56d54dba9a3f4a3dd44e",
+      "15df71eae2af42c7b31c9ca0937eab79233b9b8e",
       [
        null,
        {}
@@ -520721,49 +520717,49 @@
        ]
       ],
       "stretch-alias-block-size-001.tentative.html": [
-       "a300b664d65e83af7ec2b64d2f0c47a92828a7b6",
+       "f3ae7afd35f0680b4dada1c519ee27fc2ad8f3de",
        [
         null,
         {}
        ]
       ],
       "stretch-alias-inline-size-001.tentative.html": [
-       "a2b6fea70a4a7f1b3c59c1cfa7558003c6707aed",
+       "ca05307d2238253bdb2555f9b8ffa64f2d172e3c",
        [
         null,
         {}
        ]
       ],
       "stretch-alias-max-block-size-001.tentative.html": [
-       "31257c7647fadfd0b61d85555de1c25395375968",
+       "15d34247629e1f8d6d583208e3abc4eddd4d8271",
        [
         null,
         {}
        ]
       ],
       "stretch-alias-max-inline-size-001.tentative.html": [
-       "828bf7e1d2f83eeba7201972e760f789b09af940",
+       "7d7b6ab6ef586ffbae6046dc6c06b586ea5c6740",
        [
         null,
         {}
        ]
       ],
       "stretch-alias-min-block-size-001.tentative.html": [
-       "bc03c684b26124591c61887c8f9138ec482eccb2",
+       "c11b60609b37a332622e33e81b955e9afbdf50a6",
        [
         null,
         {}
        ]
       ],
       "stretch-alias-min-inline-size-001.tentative.html": [
-       "2af9315848f2a5a5bb5d136ccbb8441480759cb4",
+       "000511e71b70a0a5b2e83178ea6098fda52f6177",
        [
         null,
         {}
        ]
       ],
       "stretch-block-size-001.html": [
-       "c5c02ea231de1f33ecdc9859264b55ab93eac66e",
+       "c2cac98d3a57bfaea455e219341c3f77242659c9",
        [
         null,
         {}
@@ -520784,7 +520780,7 @@
        ]
       ],
       "stretch-inline-size-001.html": [
-       "ee59a7cfd355e598cc343518f154e0e02ee031e1",
+       "30fcdafd16e56cf2ead68c1a859d9fa5c086608a",
        [
         null,
         {}
@@ -520805,28 +520801,28 @@
        ]
       ],
       "stretch-max-block-size-001.html": [
-       "f836e84fcae60feb51c21c616cc5b4bc4e070fc4",
+       "26eae5f68811df7ae0a5a1662381e3bece38787f",
        [
         null,
         {}
        ]
       ],
       "stretch-max-inline-size-001.html": [
-       "e17bc9a9cd94f675fa45ea41a4d1f6c7f9bfd074",
+       "4c356c1a52d97025ff56fd4cdd49ab609f4a5799",
        [
         null,
         {}
        ]
       ],
       "stretch-min-block-size-001.html": [
-       "c7fc12728531311840e57f2b46fbd28e47327b94",
+       "ec023a34fb28798b5d5aee18e19edefb22500f81",
        [
         null,
         {}
        ]
       ],
       "stretch-min-inline-size-001.html": [
-       "60c01030f54025831e2f14e72b9ecba54371e798",
+       "28c1cc93be32bad5d59b852c061db48e6afd5bad",
        [
         null,
         {}
@@ -663712,6 +663708,15 @@
       }
      ]
     ],
+    "roles-minimum.tentative.html": [
+     "2093620bb36598b38e0caa353b5014740aeb6aa4",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ],
     "roles.html": [
      "9ef8e467bf116de7e8c7c699c6fc3cd1f5d646f2",
      [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/keyword-sizes-on-flex-item-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/keyword-sizes-on-flex-item-001.html
index 04c4bdd..15df71e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/keyword-sizes-on-flex-item-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/keyword-sizes-on-flex-item-001.html
@@ -131,17 +131,17 @@
 
 <!-- Indefinite stretch -->
 <div class="wrapper" style="width: 100px; max-height: 100px">
-  <div class="test height stretch" data-expected-height="30">X X</div>
-  <div class="test height stretch" data-expected-height="30">XXX XXX</div>
-  <div class="test height stretch" data-expected-height="30">XXXXX XXXXX</div>
+  <div class="test height stretch" data-expected-height="90">X X</div>
+  <div class="test height stretch" data-expected-height="90">XXX XXX</div>
+  <div class="test height stretch" data-expected-height="90">XXXXX XXXXX</div>
 
-  <div class="test min-height stretch" data-expected-height="10">X X</div>
-  <div class="test min-height stretch" data-expected-height="10">XXX XXX</div>
-  <div class="test min-height stretch" data-expected-height="10">XXXXX XXXXX</div>
+  <div class="test min-height stretch" data-expected-height="90">X X</div>
+  <div class="test min-height stretch" data-expected-height="90">XXX XXX</div>
+  <div class="test min-height stretch" data-expected-height="90">XXXXX XXXXX</div>
 
-  <div class="test max-height stretch" data-expected-height="510">X X</div>
-  <div class="test max-height stretch" data-expected-height="510">XXX XXX</div>
-  <div class="test max-height stretch" data-expected-height="510">XXXXX XXXXX</div>
+  <div class="test max-height stretch" data-expected-height="90">X X</div>
+  <div class="test max-height stretch" data-expected-height="90">XXX XXX</div>
+  <div class="test max-height stretch" data-expected-height="90">XXXXX XXXXX</div>
 </div>
 
 <!-- Fit-content with indefinite stretch -->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-block-size-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-block-size-001.tentative.html
index a300b664d..f3ae7af 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-block-size-001.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-block-size-001.tentative.html
@@ -193,10 +193,9 @@
     <div class="test" data-expected-height="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: row wrap">
-    <div class="test" data-expected-height="45"></div>
+    <div class="test" data-expected-height="55"></div>
     <div style="block-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-inline-size-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-inline-size-001.tentative.html
index a2b6fea7..ca05307 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-inline-size-001.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-inline-size-001.tentative.html
@@ -193,10 +193,9 @@
     <div class="test" data-expected-width="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: column wrap">
-    <div class="test" data-expected-width="45"></div>
+    <div class="test" data-expected-width="55"></div>
     <div style="inline-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-max-block-size-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-max-block-size-001.tentative.html
index 31257c7..15d34247 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-max-block-size-001.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-max-block-size-001.tentative.html
@@ -202,10 +202,9 @@
     <div class="test" data-expected-height="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: row wrap">
-    <div class="test" data-expected-height="45"></div>
+    <div class="test" data-expected-height="55"></div>
     <div style="block-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-max-inline-size-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-max-inline-size-001.tentative.html
index 828bf7e..7d7b6ab 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-max-inline-size-001.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-max-inline-size-001.tentative.html
@@ -202,10 +202,9 @@
     <div class="test" data-expected-width="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: column wrap">
-    <div class="test" data-expected-width="45"></div>
+    <div class="test" data-expected-width="55"></div>
     <div style="inline-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-min-block-size-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-min-block-size-001.tentative.html
index bc03c68..c11b6060 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-min-block-size-001.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-min-block-size-001.tentative.html
@@ -196,10 +196,9 @@
     <div class="test" data-expected-height="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: row wrap">
-    <div class="test" data-expected-height="45"></div>
+    <div class="test" data-expected-height="55"></div>
     <div style="block-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-min-inline-size-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-min-inline-size-001.tentative.html
index 2af93158..000511e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-min-inline-size-001.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-alias-min-inline-size-001.tentative.html
@@ -196,10 +196,9 @@
     <div class="test" data-expected-width="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: column wrap">
-    <div class="test" data-expected-width="45"></div>
+    <div class="test" data-expected-width="55"></div>
     <div style="inline-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-block-size-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-block-size-001.html
index c5c02ea..c2cac98d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-block-size-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-block-size-001.html
@@ -187,10 +187,9 @@
     <div class="test" data-expected-height="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: row wrap">
-    <div class="test" data-expected-height="45"></div>
+    <div class="test" data-expected-height="55"></div>
     <div style="block-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-inline-size-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-inline-size-001.html
index ee59a7cf..30fcdaf 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-inline-size-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-inline-size-001.html
@@ -187,10 +187,9 @@
     <div class="test" data-expected-width="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: column wrap">
-    <div class="test" data-expected-width="45"></div>
+    <div class="test" data-expected-width="55"></div>
     <div style="inline-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-max-block-size-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-max-block-size-001.html
index f836e84..26eae5f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-max-block-size-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-max-block-size-001.html
@@ -196,10 +196,9 @@
     <div class="test" data-expected-height="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: row wrap">
-    <div class="test" data-expected-height="45"></div>
+    <div class="test" data-expected-height="55"></div>
     <div style="block-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-max-inline-size-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-max-inline-size-001.html
index e17bc9a9..4c356c1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-max-inline-size-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-max-inline-size-001.html
@@ -196,10 +196,9 @@
     <div class="test" data-expected-width="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: column wrap">
-    <div class="test" data-expected-width="45"></div>
+    <div class="test" data-expected-width="55"></div>
     <div style="inline-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-min-block-size-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-min-block-size-001.html
index c7fc127..ec023a3 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-min-block-size-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-min-block-size-001.html
@@ -190,10 +190,9 @@
     <div class="test" data-expected-height="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: row wrap">
-    <div class="test" data-expected-height="45"></div>
+    <div class="test" data-expected-height="55"></div>
     <div style="block-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-min-inline-size-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-min-inline-size-001.html
index 60c01030..28c1cc9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-min-inline-size-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-min-inline-size-001.html
@@ -190,10 +190,9 @@
     <div class="test" data-expected-width="45"></div>
   </div>
   <!--...now with an extra-large sibling sharing the child's flex line, to be
-      sure our 'stretch' sizing value still resolves against the container size
-      rather than the flex line's size: -->
+      sure our 'stretch' sizing value resolves against the flex line's size: -->
   <div class="cb" style="display: inline-flex; flex-flow: column wrap">
-    <div class="test" data-expected-width="45"></div>
+    <div class="test" data-expected-width="55"></div>
     <div style="inline-size: 60px"></div>
   </div>
 
diff --git a/third_party/blink/web_tests/external/wpt/html-aam/roles-minimum.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/html-aam/roles-minimum.tentative-expected.txt
new file mode 100644
index 0000000..24cbf65c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html-aam/roles-minimum.tentative-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+[FAIL] el-div-autofocus-attr-role-generic
+  assert_equals: <div data-testname="el-div-autofocus-attr-role-generic" data-expectedrole="group" autofocus="" role="generic" class="ex">div with explicit role=generic and autofocus attribute.</div> expected "group" but got "generic"
+[FAIL] el-div-autofocus-attr-role-none
+  assert_equals: <div data-testname="el-div-autofocus-attr-role-none" data-expectedrole="group" autofocus="" role="none" class="ex">div with explicit role=none and autofocus attribute.</div> expected "group" but got "none"
+[FAIL] el-div-draggable-attr-role-generic
+  assert_equals: <div data-testname="el-div-draggable-attr-role-generic" data-expectedrole="group" draggable="" role="generic" class="ex">div with explicit role=generic and draggable attribute.</div> expected "group" but got "generic"
+[FAIL] el-div-draggable-attr-role-none
+  assert_equals: <div data-testname="el-div-draggable-attr-role-none" data-expectedrole="group" draggable="" role="none" class="ex">div with explicit role=none and draggable attribute.</div> expected "group" but got "none"
+[FAIL] el-div-popover-attr
+  assert_equals: <div data-testname="el-div-popover-attr" id="pd" data-expectedrole="group" popover="" class="ex">div with popover attribute, role=group</div> expected "group" but got "none"
+[FAIL] el-div-draggable-attr-invalid-role
+  assert_equals: <div data-testname="el-div-draggable-attr-invalid-role" data-expectedrole="group" draggable="" role="foo" class="ex">div with draggable attribute and errant role attribute value.</div> expected "group" but got "generic"
+[FAIL] el-section-draggable-attr
+  assert_equals: <section data-testname="el-section-draggable-attr" data-expectedrole="group" draggable="" class="ex">unnamed section with draggable attribute.</section> expected "group" but got "generic"
+[FAIL] el-cite-draggable-attr
+  assert_equals: <cite data-testname="el-cite-draggable-attr" data-expectedrole="html-cite" draggable="" class="ex">cite element with draggable attribute.</cite> expected "html-cite" but got "none"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html-aam/roles-minimum.tentative.html b/third_party/blink/web_tests/external/wpt/html-aam/roles-minimum.tentative.html
new file mode 100644
index 0000000..2093620
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html-aam/roles-minimum.tentative.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html lang=en>
+<head>
+  <title>HTML-AAM Minimum Role Verification Tests</title>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/resources/testdriver.js"></script>
+  <script src="/resources/testdriver-vendor.js"></script>
+  <script src="/resources/testdriver-actions.js"></script>
+  <script src="/wai-aria/scripts/aria-utils.js"></script>
+</head>
+<body>
+
+  <p>Tests minimum computedrole mappings defined in <a href="https://w3c.github.io/html-aam/#minimum-role">HTML-AAM</a> ([HTML-AAM Issue #454](https://github.com/w3c/html-aam/pull/454)), where the returned computed role for generic elements is expected to change based on the generic elements having attributes that would require the element no longer be generic, and potentially ignored by browsers. Most test names correspond to a unique ID defined in the spec.<p>
+
+  <!-- el-div -->
+  <div data-testname="el-div" data-expectedrole="generic" class="ex">Baseline div, role=generic</div>
+
+  <div data-testname="el-div-autofocus-attr" data-expectedrole="group" autofocus class="ex">div with autofocus attribute.</div>
+  <div data-testname="el-div-autofocus-attr-role-generic" data-expectedrole="group" autofocus role="generic" class="ex">div with explicit role=generic and autofocus attribute.</div>
+  <div data-testname="el-div-autofocus-attr-role-none" data-expectedrole="group" autofocus role="none" class="ex">div with explicit role=none and autofocus attribute.</div>
+
+  <div data-testname="el-div-draggable-attr" data-expectedrole="group" draggable class="ex">div with draggable attribute.</div>
+  <div data-testname="el-div-draggable-attr-role-generic" data-expectedrole="group" draggable role="generic" class="ex">div with explicit role=generic and draggable attribute.</div>
+  <div data-testname="el-div-draggable-attr-role-none" data-expectedrole="group" draggable role="none" class="ex">div with explicit role=none and draggable attribute.</div>
+
+  <button popovertarget=pd>Show popover div</button>
+  <div data-testname="el-div-popover-attr" id=pd data-expectedrole="group" popover class="ex">div with popover attribute, role=group</div>
+
+  <div data-testname="el-div-draggable-attr-invalid-role" data-expectedrole="group" draggable role="foo" class="ex">div with draggable attribute and errant role attribute value.</div>
+
+  <!-- el-section - an unnamed section element is a generic, not a region. so when unnamed it would have a minimum role applied -->
+  <section data-testname="el-section-draggable-attr" data-expectedrole="group" draggable class="ex">unnamed section with draggable attribute.</section>
+
+
+  <!--
+    The following tests do not meet the conditions for a minimum role change
+  -->
+  <!-- explicit valid role is used, so minimum role does not apply -->
+  <div data-testname="el-div-draggable-attr-role-article" data-expectedrole="article" role="article" draggable class="ex">div with explicit role=article and draggable attribute.</div>
+
+  <!-- implicit non-generic or none/presentational role, so minimum role does not apply -->
+  <article data-testname="el-article-draggable-attr" data-expectedrole="article" draggable class="ex">article element with draggable attribute.</article>
+  <section data-testname="el-section-named-draggable-attr" data-expectedrole="region" aria-label="test" draggable class="ex">named section with draggable attribute.</section>
+
+  <!-- element has implicit platform computedrole, so minimum role does not apply -->
+  <cite data-testname="el-cite-draggable-attr" data-expectedrole="html-cite" draggable class="ex">cite element with draggable attribute.</cite>
+
+
+  <script>
+    AriaUtils.verifyRolesBySelector(".ex");
+  </script>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js
index aeebc67..43e5d1a 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js
@@ -1676,6 +1676,89 @@
       }
     }
   },
+  {
+    'name': 'quantized softmax',
+    'graph': {
+      'inputs': {
+        'input': {
+          'data': [
+            0.6811466217041016, 0.0479511022567749, 0.33355462551116943,
+            0.19882695376873016, 0.41167140007019043, 0.07934240251779556,
+          ],
+          'descriptor': {shape: [2, 3], dataType: 'float32'},
+          'constant': false
+        },
+        'inputScale': {
+          'data': [0.00390625],
+          'descriptor': {shape: [1], dataType: 'float32'},
+          'constant': true
+        },
+        'inputZeroPoint': {
+          'data': [-128],
+          'descriptor': {shape: [1], dataType: 'int8'},
+          'constant': true
+        },
+        'outputScale': {
+          'data': [0.00390625],
+          'descriptor': {shape: [1], dataType: 'float32'},
+          'constant': true
+        },
+        'outputZeroPoint': {
+          'data': [-128],
+          'descriptor': {shape: [1], dataType: 'int8'},
+          'constant': true
+        },
+      },
+      'operators': [
+        {
+          'name': 'quantizeLinear',
+          'arguments': [
+            {'input': 'input'},
+            {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+          ],
+          'outputs': 'quantizedInput'
+        },
+        {
+          'name': 'dequantizeLinear',
+          'arguments': [
+            {'input': 'quantizedInput'},
+            {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+          ],
+          'outputs': 'dequantizedInput'
+        },
+        {
+          'name': 'softmax',
+          'arguments': [{'input': 'dequantizedInput'}, {'axis': 0}],
+          'outputs': 'softmaxOutput'
+        },
+        {
+          'name': 'quantizeLinear',
+          'arguments': [
+            {'input': 'softmaxOutput'},
+            {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+          ],
+          'outputs': 'quantizedSoftmaxOutput'
+        },
+        {
+          'name': 'dequantizeLinear',
+          'arguments': [
+            {'input': 'quantizedSoftmaxOutput'},
+            {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+          ],
+          'outputs': 'output'
+        }
+      ],
+      'expectedOutputs': {
+        'output': {
+          'data': [
+            0.6171875, 0.41015625, 0.5625,
+            0.3828125, 0.58984375, 0.4375,
+          ],
+          'descriptor': {shape: [2, 3], dataType: 'float32'}
+        }
+      }
+    }
+  },
 ];
 
 if (navigator.ml) {
diff --git a/third_party/blink/web_tests/http/tests/media/media-source/mediasource-addsourcebuffer-chrome.html b/third_party/blink/web_tests/http/tests/media/media-source/mediasource-addsourcebuffer-chrome.html
index 92a1680..29f2b46 100644
--- a/third_party/blink/web_tests/http/tests/media/media-source/mediasource-addsourcebuffer-chrome.html
+++ b/third_party/blink/web_tests/http/tests/media/media-source/mediasource-addsourcebuffer-chrome.html
@@ -4,6 +4,7 @@
         <script src="/w3c/resources/testharness.js"></script>
         <script src="/w3c/resources/testharnessreport.js"></script>
         <script src="mediasource-util.js"></script>
+        <script src="../../resources/quota-exceeded-helper.js"></script>
 
         <link rel='stylesheet' href='/w3c/resources/testharness.css'>
     </head>
@@ -21,8 +22,9 @@
               sourceBuffer.appendBuffer(mediaData);
               test.waitForExpectedEvents(function()
               {
-                  assert_throws_dom("QuotaExceededError",
+                  assert_throws_quota_exceeded(
                       function() { mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE); },
+                      /*quota=*/ null, /*requested=*/ null,
                       "addSourceBuffer must throw an exception if the MediaSource has already received init segments for all sourcebuffers added at the time");
                   test.done();
               });
@@ -51,8 +53,9 @@
               {
                   // MediaSource is fully initialized now, so adding new SourceBuffers is no longer possible.
                   assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_METADATA);
-                  assert_throws_dom("QuotaExceededError",
+                  assert_throws_quota_exceeded(
                       function() { mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE); },
+                      /*quota=*/ null, /*requested=*/ null,
                       "addSourceBuffer must throw an exception if the media element has already reached HAVE_METADATA");
                   test.done();
               });
diff --git a/third_party/blink/web_tests/http/tests/resources/quota-exceeded-helper.js b/third_party/blink/web_tests/http/tests/resources/quota-exceeded-helper.js
new file mode 100644
index 0000000..b10c446
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/resources/quota-exceeded-helper.js
@@ -0,0 +1,55 @@
+/**
+ * Assert a QuotaExceededError with the expected parameters are thrown.
+ *
+ * @param {Function} func Function which should throw.
+ * @param {number} quota - The expected quota value.
+ * @param {number} requested - The expected requested value.
+ * @param {string} description - Description of the condition being tested.
+ *
+ */
+function assert_throws_quota_exceeded(func, quota, requested, description) {
+  try {
+    func.call(this);
+    throw new Error(make_error(description, `${func} did not throw`));
+  } catch (e) {
+    // Basic sanity-checks on the thrown exception.
+    if (typeof e !== 'object') {
+      throw new Error(make_error(
+          description,
+          `${func} threw ${e} with type ${typeof e}, not an object`));
+    }
+    if (typeof e === null) {
+      throw new Error(
+          make_error(description, `${func} threw null, not an object`));
+    }
+
+    // Check that the exception and their properties match.
+    if (e.name !== 'QuotaExceededError') {
+      throw new Error(make_error(
+          description, `${func} threw ${e} that is not a QuotaExceptionError`));
+    }
+    if (e.quota !== quota) {
+      throw new Error(make_error(
+          description,
+          `${func} threw ${e} with quota ${e.quota}, expected ${quota}`));
+    }
+    if (e.requested !== requested) {
+      throw new Error(make_error(
+          description,
+          `${func} threw ${e} with requested ${e.requested},` +
+              ` expected ${requested}`));
+    }
+
+    // Check that the exception is from the right global.  This check is
+    // last so more specific, and more informative, checks on the properties
+    // can happen in case a totally incorrect exception is thrown.
+    if (e.constructor !== self.QuotaExceededError) {
+      throw new Error(make_error(
+          description, `${func} threw an exception from the wrong global`));
+    }
+  }
+}
+
+function make_error(description, error) {
+  return `assert_throws_quota_exceeded: ${description} ${error}`
+}
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/background-fetch/fetch.https.window-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/background-fetch/fetch.https.window-expected.txt
index cad6dcc..70e272c8 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/background-fetch/fetch.https.window-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/background-fetch/fetch.https.window-expected.txt
@@ -4,6 +4,6 @@
 [FAIL] Requests with text/json content type require CORS Preflight and succeed.
   assert_equals: expected "backgroundfetchsuccess" but got "backgroundfetchfail"
 [FAIL] Background Fetch that exceeds the quota throws a QuotaExceededError
-  promise_rejects_dom: This fetch should have thrown a quota exceeded error function "function() { throw e; }" threw object "QuotaExceededError: Failed to execute 'fetch' on 'BackgroundFetchManager': Quota exceeded." that is not a DOMException QUOTA_EXCEEDED_ERR: property "code" is equal to 0, expected 22
+  promise_rejects_dom: This fetch should have thrown a quota exceeded error function "function() { throw e; }" threw object "QuotaExceededError: Quota exceeded." that is not a DOMException QUOTA_EXCEEDED_ERR: property "code" is equal to 0, expected 22
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/background-fetch/fetch.https.window-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/background-fetch/fetch.https.window-expected.txt
index cad6dcc..70e272c8 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/background-fetch/fetch.https.window-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/background-fetch/fetch.https.window-expected.txt
@@ -4,6 +4,6 @@
 [FAIL] Requests with text/json content type require CORS Preflight and succeed.
   assert_equals: expected "backgroundfetchsuccess" but got "backgroundfetchfail"
 [FAIL] Background Fetch that exceeds the quota throws a QuotaExceededError
-  promise_rejects_dom: This fetch should have thrown a quota exceeded error function "function() { throw e; }" threw object "QuotaExceededError: Failed to execute 'fetch' on 'BackgroundFetchManager': Quota exceeded." that is not a DOMException QUOTA_EXCEEDED_ERR: property "code" is equal to 0, expected 22
+  promise_rejects_dom: This fetch should have thrown a quota exceeded error function "function() { throw e; }" threw object "QuotaExceededError: Quota exceeded." that is not a DOMException QUOTA_EXCEEDED_ERR: property "code" is equal to 0, expected 22
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 791460a0..93a42765 100644
--- a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -1595,7 +1595,6 @@
     getter referrer
     getter referrerPolicy
     getter signal
-    getter targetAddressSpace
     getter url
     method arrayBuffer
     method blob
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 8f5c13d..aa7e68a 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -1727,7 +1727,6 @@
 [Worker]     getter referrer
 [Worker]     getter referrerPolicy
 [Worker]     getter signal
-[Worker]     getter targetAddressSpace
 [Worker]     getter url
 [Worker]     method arrayBuffer
 [Worker]     method blob
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 89f5441..12a4feb 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -7462,7 +7462,6 @@
     getter referrer
     getter referrerPolicy
     getter signal
-    getter targetAddressSpace
     getter url
     method arrayBuffer
     method blob
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
index e2724240..5aeed0c0 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -1514,7 +1514,6 @@
 [Worker]     getter referrer
 [Worker]     getter referrerPolicy
 [Worker]     getter signal
-[Worker]     getter targetAddressSpace
 [Worker]     getter url
 [Worker]     method arrayBuffer
 [Worker]     method blob
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElement/tex-element-2d-basic.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElement/tex-element-2d-basic.html
new file mode 100644
index 0000000..8745476
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElement/tex-element-2d-basic.html
@@ -0,0 +1,194 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>Ensure drawElement2D in texImage2D</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <style>
+      .top-left {
+        position: absolute;
+        width: 16px;
+        height: 16px;
+        top: 0px;
+        left: 0px;
+        background-color:red;
+      }
+      .bottom-left {
+        position: absolute;
+        width: 16px;
+        height: 16px;
+        top: 16px;
+        left: 0px;
+        background-color:blue;
+      }
+      .top-right {
+        position: absolute;
+        width: 16px;
+        height: 16px;
+        top: 0px;
+        left: 16px;
+        background-color:green;
+      }
+      .bottom-right {
+        position: absolute;
+        width: 16px;
+        height: 16px;
+        top: 16px;
+        left: 16px;
+        background-color:gray;
+      }
+      #container {
+        position:absolute;
+        left:0px;
+        top:0px;
+        width:32px;
+        height:32px;
+      }
+    </style>
+  </head>
+  <body>
+    <canvas id="canvasHost" width="32" height="32" layoutsubtree=true>
+      <div id="container">
+        <div class="top-left"></div>
+        <div class="bottom-left"></div>
+        <div class="top-right"></div>
+        <div class="bottom-right"></div>
+      </div>
+    </canvas>
+    <script>
+      function runTest() {
+        async_test(t => {
+          const gl = canvasHost.getContext("webgl2");
+          if (!gl) {
+            assert_unreached("webgl2 context not available");
+            t.done();
+          }
+
+          gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
+
+          const tex = gl.createTexture();
+          gl.bindTexture(gl.TEXTURE_2D, tex);
+          const level = 0;
+          const internalformat = gl.RGBA;
+          const format = gl.RGBA;
+          const type = gl.UNSIGNED_BYTE;
+          gl.texElement2D(gl.TEXTURE_2D, level, internalformat, format, type, container);
+
+          const fbo = gl.createFramebuffer();
+          gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+          gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+
+          let complete = gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE;
+          assert_true(complete);
+
+          const pixels = new Uint8Array(32 * 32 * 4);
+          gl.readPixels(0, 0, 32, 32, format, type, pixels);
+
+          // Check the bottom row
+          assert_equals(pixels[0], 0, "bottom left of bottom left red");
+          assert_equals(pixels[1], 0, "bottom left of bottom left green");
+          assert_equals(pixels[2], 255, "bottom left of bottom left blue");
+          assert_equals(pixels[3], 255, "bottom left of bottom left alpha");
+
+          let offset = 15 * 4;
+          assert_equals(pixels[offset], 0, "bottom right of bottom left red");
+          assert_equals(pixels[offset+1], 0, "bottom right of bottom left green");
+          assert_equals(pixels[offset+2], 255, "bottom right of bottom left blue");
+          assert_equals(pixels[offset+3], 255, "bottom right of bottom left alpha");
+
+          offset += 4;
+          assert_equals(pixels[offset], 128, "bottom left of bottom right red");
+          assert_equals(pixels[offset+1], 128, "bottom left of bottom right green");
+          assert_equals(pixels[offset+2], 128, "bottom left of bottom right blue");
+          assert_equals(pixels[offset+3], 255, "bottom left of bottom right alpha");
+
+          offset = 31 * 4;
+          assert_equals(pixels[offset], 128, "bottom right of bottom right red");
+          assert_equals(pixels[offset+1], 128, "bottom right of bottom right green");
+          assert_equals(pixels[offset+2], 128, "bottom right of bottom right blue");
+          assert_equals(pixels[offset+3], 255, "bottom right of bottom right alpha");
+
+          // Now just below the middle row
+          offset = 15 * (32 * 4);
+          assert_equals(pixels[0], 0, "top left of bottom left red");
+          assert_equals(pixels[1], 0, "top left of bottom left green");
+          assert_equals(pixels[2], 255, "top left of bottom left blue");
+          assert_equals(pixels[3], 255, "top left of bottom left alpha");
+
+          offset += 15 * 4;
+          assert_equals(pixels[offset], 0, "top right of bottom left red");
+          assert_equals(pixels[offset+1], 0, "top right of bottom left green");
+          assert_equals(pixels[offset+2], 255, "top right of bottom left blue");
+          assert_equals(pixels[offset+3], 255, "top right of bottom left alpha");
+
+          offset += 4;
+          assert_equals(pixels[offset], 128, "top left of bottom right red");
+          assert_equals(pixels[offset+1], 128, "top left of bottom right red");
+          assert_equals(pixels[offset+2], 128, "top left of bottom right red");
+          assert_equals(pixels[offset+3], 255, "top left of bottom right red");
+
+          offset += 14 * 4;
+          assert_equals(pixels[offset], 128, "top right of bottom right red");
+          assert_equals(pixels[offset+1], 128, "top right of bottom right red");
+          assert_equals(pixels[offset+2], 128, "top right of bottom right red");
+          assert_equals(pixels[offset+3], 255, "top right of bottom right red");
+
+          // Just above the middle row
+          offset = 16 * (32 * 4);
+          assert_equals(pixels[offset], 255, "bottom left of top left red");
+          assert_equals(pixels[offset+1], 0, "bottom left of top left green");
+          assert_equals(pixels[offset+2], 0, "bottom left of top left blue");
+          assert_equals(pixels[offset+3], 255), "bottom left of top left alpha";
+
+          offset += 15 * 4;
+          assert_equals(pixels[offset], 255, "bottom right of top left red");
+          assert_equals(pixels[offset+1], 0, "bottom right of top left green");
+          assert_equals(pixels[offset+2], 0, "bottom right of top left blue");
+          assert_equals(pixels[offset+3], 255, "bottom right of top left alpha");
+
+          offset += 4;
+          assert_equals(pixels[offset], 0, "bottom left of top right red");
+          assert_equals(pixels[offset+1], 128, "bottom left of top right green");
+          assert_equals(pixels[offset+2], 0, "bottom left of top right blue");
+          assert_equals(pixels[offset+3], 255, "bottom left of top right alpa");
+
+          offset += 14 * 4;
+          assert_equals(pixels[offset], 0, "bottom right of top right red");
+          assert_equals(pixels[offset+1], 128, "bottom right of top right green");
+          assert_equals(pixels[offset+2], 0, "bottom right of top right blue");
+          assert_equals(pixels[offset+3], 255, "bottom right of top right alpha");
+
+          // Top row
+          offset = 31 * 32 * 4;
+          assert_equals(pixels[offset], 255, "top left of top left red");
+          assert_equals(pixels[offset+1], 0, "top left of top left green");
+          assert_equals(pixels[offset+2], 0, "top left of top left blue");
+          assert_equals(pixels[offset+3], 255), "top left of top left alpha";
+
+          offset += 15 * 4;
+          assert_equals(pixels[offset], 255, "top right of top left red");
+          assert_equals(pixels[offset+1], 0, "top right of top left green");
+          assert_equals(pixels[offset+2], 0, "top right of top left blue");
+          assert_equals(pixels[offset+3], 255, "top right of top left alpha");
+
+          offset += 4;
+          assert_equals(pixels[offset], 0, "top left of top right red");
+          assert_equals(pixels[offset+1], 128, "top left of top right green");
+          assert_equals(pixels[offset+2], 0, "top left of top right blue");
+          assert_equals(pixels[offset+3], 255, "top left of top right alpa");
+
+          offset += 14 * 4;
+          assert_equals(pixels[offset], 0, "top right of top right red");
+          assert_equals(pixels[offset+1], 128, "top right of top right green");
+          assert_equals(pixels[offset+2], 0, "top right of top right blue");
+          assert_equals(pixels[offset+3], 255, "top right of top right alpha");
+
+          t.done();
+        }, "verify the texElement2D works as expected");
+      }
+
+      onload = () => runTest();
+    </script>
+  </body>
+</html>
diff --git a/third_party/boringssl/src b/third_party/boringssl/src
index a934ee9..be17ebd 160000
--- a/third_party/boringssl/src
+++ b/third_party/boringssl/src
@@ -1 +1 @@
-Subproject commit a934ee9e1fe4397e682f9f18b1f4f061d7400f9d
+Subproject commit be17ebd7c19cd9f60ff684a953329a25a531a688
diff --git a/third_party/crossbench b/third_party/crossbench
index 6b953c0b..f32ec85 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit 6b953c0b98c81c00f8458266d07b570f58b40848
+Subproject commit f32ec85696ba0d27ecf46a56277f53570b562021
diff --git a/third_party/dawn b/third_party/dawn
index 5c36eda..8a398ff 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 5c36eda233b37810c8ca6ad00ad0300b0f821890
+Subproject commit 8a398ff51641a55c6f366211ad42764bd526bad5
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index fa13103..79db863 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit fa131039ce7473c0f2f6f18c6039e2a9a72b35a4
+Subproject commit 79db863aa9b069c683596bd63a754e1ce4f06f90
diff --git a/third_party/glslang/src b/third_party/glslang/src
index 9635880..5cffd9a 160000
--- a/third_party/glslang/src
+++ b/third_party/glslang/src
@@ -1 +1 @@
-Subproject commit 963588074b26326ff0426c8953c1235213309bdb
+Subproject commit 5cffd9a796210165631c995f9f58787ea0527eea
diff --git a/third_party/googletest/src b/third_party/googletest/src
index fa8438a..16d4f8e 160000
--- a/third_party/googletest/src
+++ b/third_party/googletest/src
@@ -1 +1 @@
-Subproject commit fa8438ae6b70c57010177de47a9f13d7041a6328
+Subproject commit 16d4f8eff6d7cefca6975d82a53f8fc995a6feb7
diff --git a/third_party/mediapipe/src/mediapipe/framework/scheduler.h b/third_party/mediapipe/src/mediapipe/framework/scheduler.h
index 549695ee..1c391e94 100644
--- a/third_party/mediapipe/src/mediapipe/framework/scheduler.h
+++ b/third_party/mediapipe/src/mediapipe/framework/scheduler.h
@@ -16,6 +16,7 @@
 #define MEDIAPIPE_FRAMEWORK_SCHEDULER_H_
 
 #include <atomic>
+#include <deque>
 #include <functional>
 #include <map>
 #include <memory>
diff --git a/third_party/mediapipe/src/mediapipe/framework/scheduler_shared.h b/third_party/mediapipe/src/mediapipe/framework/scheduler_shared.h
index 3e5c7217..f8b5d86 100644
--- a/third_party/mediapipe/src/mediapipe/framework/scheduler_shared.h
+++ b/third_party/mediapipe/src/mediapipe/framework/scheduler_shared.h
@@ -19,7 +19,6 @@
 #include <cstdint>
 #include <functional>
 #include <memory>
-#include <queue>
 #include <utility>
 
 #include "absl/base/macros.h"
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn
index f15292de..d538beac 100644
--- a/third_party/nearby/BUILD.gn
+++ b/third_party/nearby/BUILD.gn
@@ -142,6 +142,7 @@
     ":nearby_defines",
   ]
   sources = [
+    "src/connections/implementation/awdl_bwu_handler.cc",
     "src/connections/implementation/awdl_endpoint_channel.cc",
     "src/connections/implementation/base_bwu_handler.cc",
     "src/connections/implementation/base_endpoint_channel.cc",
@@ -182,6 +183,7 @@
     "src/connections/implementation/wifi_lan_service_info.cc",
   ]
   public = [
+    "src/connections/implementation/awdl_bwu_handler.h",
     "src/connections/implementation/awdl_endpoint_channel.h",
     "src/connections/implementation/base_bwu_handler.h",
     "src/connections/implementation/base_endpoint_channel.h",
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index b4ad4b9..482e7f903 100644
--- a/third_party/nearby/README.chromium
+++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@
 Name: Nearby Connections Library
 Short Name: Nearby
 URL: https://github.com/google/nearby
-Version: 59e527dbe3326f52bce1bda3dc30e997a68f2443
+Version: 959322177f40f2e0f1ecacd8a1aea2805e67b62b
 License: Apache-2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/nearby/src b/third_party/nearby/src
index 59e527d..9593221 160000
--- a/third_party/nearby/src
+++ b/third_party/nearby/src
@@ -1 +1 @@
-Subproject commit 59e527dbe3326f52bce1bda3dc30e997a68f2443
+Subproject commit 959322177f40f2e0f1ecacd8a1aea2805e67b62b
diff --git a/third_party/openscreen/OWNERS b/third_party/openscreen/OWNERS
index 3b189a9..af49642 100644
--- a/third_party/openscreen/OWNERS
+++ b/third_party/openscreen/OWNERS
@@ -3,4 +3,3 @@
 
 # Secondary owners
 muyaoxu@google.com
-takumif@chromium.org
diff --git a/third_party/perfetto b/third_party/perfetto
index 1882429..410e8a1 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 188242925b83331305ff6f8f146b2a6667f21ac2
+Subproject commit 410e8a174f36c6293d3aebd3833d04ac3f367b40
diff --git a/third_party/skia b/third_party/skia
index 3ead037d..db50a613 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 3ead037d837c32c819d77607e01f208e91caca07
+Subproject commit db50a61378fddb9a29efa40e0058cec56de88c1b
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src
index 66fe610..736e415 160000
--- a/third_party/spirv-tools/src
+++ b/third_party/spirv-tools/src
@@ -1 +1 @@
-Subproject commit 66fe610946a6d98169f8ebe9ca483f64c4009fa5
+Subproject commit 736e415ebaa4290d21e42e370db5e933b9dff06d
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index bb4c9a8..4f278f1c 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit bb4c9a8250cf96a68bd4c3ba51ef231a59b6c502
+Subproject commit 4f278f1c86463df2d83dddba19374c4b69384245
diff --git a/third_party/webrtc b/third_party/webrtc
index 3d059dd..4930228 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 3d059dd3ca2653d372ea4463dadc53a40baf7fa8
+Subproject commit 4930228a3eaca3411d54b315ed29e5a504ed61da
diff --git a/tools/autotest.py b/tools/autotest.py
index 9813567..15051b7 100755
--- a/tools/autotest.py
+++ b/tools/autotest.py
@@ -59,15 +59,119 @@
 # Some test suites use suffixes that would also match non-test-suite targets.
 # Those test suites should be manually added here.
 _TEST_TARGET_ALLOWLIST = [
-    # Running ash_pixeltests requires the --no-try-android-wrappers flag.
-    '//ash:ash_pixeltests',
-    '//chrome/test:browser_tests',
-    '//chrome/test:interactive_ui_tests',
-    '//chrome/test:unit_tests',
-]
 
+    # The tests below this line were output from the ripgrep command just below:
+    '//ash:ash_pixeltests',
+    '//build/rust/tests/test_serde_json_lenient:test_serde_json_lenient',
+    '//chrome/browser/apps/app_service/app_install:app_install_fuzztests',
+    '//chrome/browser/glic/e2e_test:glic_internal_e2e_interactive_ui_tests',
+    '//chrome/browser/mac:install_sh_test',
+    '//chrome/browser/metrics/perf:profile_provider_unittest',
+    '//chrome/browser/privacy_sandbox/notice:fuzz_tests',
+    '//chrome/browser/web_applications:web_application_fuzztests',
+    '//chromecast/media/base:video_plane_controller_test',
+    '//chromecast/metrics:cast_metrics_unittest',
+    '//chromecast/starboard/media/cdm:starboard_decryptor_cast_test',
+    '//chromecast/starboard/media/cdm:starboard_drm_key_tracker_test',
+    '//chromecast/starboard/media/cdm:starboard_drm_wrapper_test',
+    '//chromecast/starboard/media/media:media_pipeline_backend_starboard_test',
+    '//chromecast/starboard/media/media:mime_utils_test',
+    '//chromecast/starboard/media/media:starboard_audio_decoder_test',
+    '//chromecast/starboard/media/media:starboard_resampler_test',
+    '//chromecast/starboard/media/media:starboard_video_decoder_test',
+    '//chromecast/starboard/media/media:starboard_video_plane_test',
+    '//chrome/enterprise_companion:enterprise_companion_integration_tests',
+    '//chrome/enterprise_companion:enterprise_companion_tests',
+    '//chrome/installer/gcapi:gcapi_test',
+    '//chrome/installer/test:upgrade_test',
+    '//chromeos/ash/components/kiosk/vision:kiosk_vision_unit_tests',
+    '//chrome/test/android:chrome_public_apk_baseline_profile_generator',
+    '//chrome/test:unit_tests',
+    '//clank/javatests:chrome_apk_baseline_profile_generator',
+    '//clank/javatests:chrome_smoke_test',
+    '//clank/javatests:monochrome_bundle_smoke_test',
+    '//clank/javatests:trichrome_chrome_google_bundle_smoke_test',
+    '//components/chromeos_camera:jpeg_decode_accelerator_unittest',
+    '//components/exo/wayland:wayland_client_compatibility_tests',
+    '//components/exo/wayland:wayland_client_tests',
+    '//components/facilitated_payments/core/validation:pix_code_validator_fuzzer',
+    '//components/ip_protection:components_ip_protection_fuzztests',
+    '//components/minidump_uploader:minidump_uploader_test',
+    '//components/paint_preview/browser:paint_preview_browser_unit_tests',
+    '//components/paint_preview/common:paint_preview_common_unit_tests',
+    '//components/paint_preview/renderer:paint_preview_renderer_unit_tests',
+    '//components/services/paint_preview_compositor:paint_preview_compositor_unit_tests',
+    '//components/translate/core/language_detection:language_detection_util_fuzztest',
+    '//components/webcrypto:webcrypto_testing_fuzzer',
+    '//components/zucchini:zucchini_integration_test',
+    '//content/test/fuzzer:devtools_protocol_encoding_json_fuzzer',
+    '//fuchsia_web/runners:cast_runner_integration_tests',
+    '//fuchsia_web/webengine:web_engine_integration_tests',
+    '//google_apis/gcm:gcm_unit_tests',
+    '//gpu:gl_tests',
+    '//gpu:gpu_benchmark',
+    '//gpu/vulkan/android:vk_tests',
+    '//ios/web:ios_web_inttests',
+    '//ios/web_view:ios_web_view_inttests',
+    '//media/cdm:aes_decryptor_fuzztests',
+    '//media/formats:ac3_util_fuzzer',
+    '//media/gpu/chromeos:image_processor_test',
+    '//media/gpu/v4l2:v4l2_unittest',
+    '//media/gpu/vaapi/test/fake_libva_driver:fake_libva_driver_unittest',
+    '//media/gpu/vaapi:vaapi_unittest',
+    '//native_client/tests:large_tests',
+    '//native_client/tests:medium_tests',
+    '//native_client/tests:small_tests',
+    '//sandbox/mac:sandbox_mac_fuzztests',
+    '//sandbox/win:sbox_integration_tests',
+    '//sandbox/win:sbox_validation_tests',
+    '//testing/libfuzzer/fuzzers:libyuv_scale_fuzztest',
+    '//testing/libfuzzer/fuzzers:paint_vector_icon_fuzztest',
+    '//third_party/blink/renderer/controller:blink_perf_tests',
+    '//third_party/blink/renderer/core:css_parser_fuzzer',
+    '//third_party/blink/renderer/core:inspector_ghost_rules_fuzzer',
+    '//third_party/blink/renderer/platform/loader:unencoded_digest_fuzzer',
+    '//third_party/crc32c:crc32c_benchmark',
+    '//third_party/crc32c:crc32c_tests',
+    '//third_party/dawn/src/dawn/tests/benchmarks:dawn_benchmarks',
+    '//third_party/highway:highway_tests',
+    '//third_party/ipcz/src:ipcz_tests',
+    '//third_party/libaom:av1_encoder_fuzz_test',
+    '//third_party/libaom:test_libaom',
+    '//third_party/libvpx:test_libvpx',
+    '//third_party/libvpx:vp8_encoder_fuzz_test',
+    '//third_party/libvpx:vp9_encoder_fuzz_test',
+    '//third_party/libwebp:libwebp_advanced_api_fuzzer',
+    '//third_party/libwebp:libwebp_animation_api_fuzzer',
+    '//third_party/libwebp:libwebp_animencoder_fuzzer',
+    '//third_party/libwebp:libwebp_enc_dec_api_fuzzer',
+    '//third_party/libwebp:libwebp_huffman_fuzzer',
+    '//third_party/libwebp:libwebp_mux_demux_api_fuzzer',
+    '//third_party/libwebp:libwebp_simple_api_fuzzer',
+    '//third_party/opus:test_opus_api',
+    '//third_party/opus:test_opus_decode',
+    '//third_party/opus:test_opus_encode',
+    '//third_party/opus:test_opus_padding',
+    '//third_party/pdfium:pdfium_embeddertests',
+    '//third_party/pffft:pffft_unittest',
+    '//third_party/rapidhash:rapidhash_fuzztests',
+    '//ui/ozone:ozone_integration_tests',
+]
+"""
+ You can run this command to find test targets that do not match these regexes,
+ and use it to update _TEST_TARGET_ALLOWLIST.
+rg '^(instrumentation_test_runner|test)\("([^"]*)' -o -g'BUILD.gn' -r'$2' -N \
+  | rg -v '(_browsertests|_perftests|_wpr_tests|_unittests)$' \
+  | rg '^(.*)/BUILD.gn(.*)$' -r'\'//$1$2\',' \
+  | sort
+
+ And you can use a command like this to find source_set targets that do match
+ the test target regex (ideally this is minimal).
+rg '^source_set\("([^"]*)' -o -g'BUILD.gn' -r'$1' -N | \
+  rg '(_browsertests|_perftests|_wpr_tests|_unittests)$'
+"""
 _TEST_TARGET_REGEX = re.compile(
-    r'(_browsertests|_perftests|_wpr_tests|_unittests|_tests?)$')
+    r'(_browsertests|_perftests|_wpr_tests|_unittests)$')
 
 _PREF_MAPPING_FILE_PATTERN = re.escape(
     str(Path('components') / 'policy' / 'test' / 'data' / 'pref_mapping') +
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 24307d5..69ff7d2a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -13120,6 +13120,7 @@
   <int value="-353182790" label="ConsistentOmniboxGeolocation:disabled"/>
   <int value="-353180213" label="DevicePosture:disabled"/>
   <int value="-352731236" label="DIPS:enabled"/>
+  <int value="-352377362" label="FedCmIframeOrigin:disabled"/>
   <int value="-352196997" label="CCTGoogleBottomBar:enabled"/>
   <int value="-351830087" label="AssistantConsentSimplifiedText:enabled"/>
   <int value="-351744276"
@@ -13869,6 +13870,7 @@
   <int value="-74964571" label="EcheCustomWidget:enabled"/>
   <int value="-74808314" label="WebRtcWgcRequireBorder:enabled"/>
   <int value="-73282711" label="EnableAppListSearchAutocomplete:enabled"/>
+  <int value="-73203893" label="FedCmIframeOrigin:enabled"/>
   <int value="-72904030" label="ChromeRefresh2023:enabled"/>
   <int value="-72691146" label="SafetyHubFollowup:disabled"/>
   <int value="-72590181" label="DrawCutoutEdgeToEdge:enabled"/>
@@ -14182,6 +14184,7 @@
   <int value="46334141"
       label="FeatureNotificationGuideSkipCheckForLowEngagedUsers:enabled"/>
   <int value="48159177" label="reduced-referrer-granularity"/>
+  <int value="48619644" label="MostVisitedTilesVisualDeduplication:enabled"/>
   <int value="48666910" label="ClipboardHistoryLongpress:disabled"/>
   <int value="48850808" label="AutocorrectUseReplaceSurroundingText:enabled"/>
   <int value="49971529" label="TabGroupEntryPointsAndroid:disabled"/>
@@ -15039,6 +15042,7 @@
   <int value="366524741"
       label="ArcTrackpadScrollTouchscreenEmulation:disabled"/>
   <int value="366860338" label="InternalServerSideSpeechRecognition:enabled"/>
+  <int value="367755253" label="WebNNOnnxRuntime:disabled"/>
   <int value="367917214" label="SearchEngineChoiceTrigger:enabled"/>
   <int value="368073826" label="AnnotatorMode:enabled"/>
   <int value="368224960" label="FedCmRpContext:disabled"/>
@@ -15281,6 +15285,7 @@
   <int value="454778715"
       label="ForceSafeSearchForUnauthenticatedSupervisedUsers:disabled"/>
   <int value="455140116" label="CCTIncognitoAvailableToThirdParty:disabled"/>
+  <int value="455334174" label="MostVisitedTilesVisualDeduplication:disabled"/>
   <int value="455698038"
       label="disable-gesture-requirement-for-media-playback"/>
   <int value="455754036" label="MirroringService:disabled"/>
@@ -19639,6 +19644,7 @@
   <int value="2084913105" label="RequestDesktopSitePerSiteIph:enabled"/>
   <int value="2085186092" label="BulkPrinters:disabled"/>
   <int value="2085438501" label="ChromeHome:enabled"/>
+  <int value="2086569407" label="WebNNOnnxRuntime:enabled"/>
   <int value="2087069781" label="ui-debug-tools:enabled"/>
   <int value="2087994185" label="PasspointSettings:disabled"/>
   <int value="2088061188" label="LibinputHandleTouchpad:enabled"/>
diff --git a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
index 3512850..5e8f1aa5 100644
--- a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
@@ -448,8 +448,6 @@
 # storage
 ayui@chromium.org
 leimy@chromium.org
-# structured_metrics
-jongahn@chromium.org
 # subresource
 csharrison@chromium.org
 tbansal@chromium.org
diff --git a/tools/metrics/histograms/metadata/ai/histograms.xml b/tools/metrics/histograms/metadata/ai/histograms.xml
index 5806ae6..03acb52 100644
--- a/tools/metrics/histograms/metadata/ai/histograms.xml
+++ b/tools/metrics/histograms/metadata/ai/histograms.xml
@@ -47,6 +47,50 @@
   <variant name="Writer" summary="AI writer session"/>
 </variants>
 
+<histogram name="AI.Session.{SessionType}.ContextTime" units="ms"
+    expires_after="2026-05-19">
+  <owner>cduvall@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    Measures the time it takes to process prompt input. This is recorded each
+    time a prompt is added.
+  </summary>
+  <token key="SessionType" variants="SessionType"/>
+</histogram>
+
+<histogram name="AI.Session.{SessionType}.ContextTokens" units="tokens"
+    expires_after="2026-05-19">
+  <owner>cduvall@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    Measures the size of the prompt in tokens. This is recorded each time a
+    prompt is added.
+  </summary>
+  <token key="SessionType" variants="SessionType"/>
+</histogram>
+
+<histogram name="AI.Session.{SessionType}.Crashed" enum="BooleanYesNo"
+    expires_after="2026-05-19">
+  <owner>cduvall@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    Records whether the model crashed while a session was active. Recorded each
+    time a session is destroyed.
+  </summary>
+  <token key="SessionType" variants="SessionType"/>
+</histogram>
+
+<histogram name="AI.Session.{SessionType}.FirstResponseTime" units="ms"
+    expires_after="2026-05-19">
+  <owner>cduvall@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    Measures the time it takes until the first tokens of the response are
+    received. This is recorded each time the model gives a response.
+  </summary>
+  <token key="SessionType" variants="SessionType"/>
+</histogram>
+
 <histogram name="AI.Session.{SessionType}.PromptRequestSize" units="bytes"
     expires_after="2025-10-27">
   <owner>leimy@chromium.org</owner>
@@ -95,6 +139,28 @@
   <token key="SessionType" variants="SessionType"/>
 </histogram>
 
+<histogram name="AI.Session.{SessionType}.ResponseCompleteTime" units="ms"
+    expires_after="2026-05-19">
+  <owner>cduvall@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    Measures the time it takes until the full response is complete. This is
+    recorded each time the model gives a response.
+  </summary>
+  <token key="SessionType" variants="SessionType"/>
+</histogram>
+
+<histogram name="AI.Session.{SessionType}.ResponseTokens" units="tokens"
+    expires_after="2026-05-19">
+  <owner>cduvall@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    Measures the number of tokens in the completed response. This is recorded
+    each time the model gives a response.
+  </summary>
+  <token key="SessionType" variants="SessionType"/>
+</histogram>
+
 <histogram
     name="AI.Summarizer.APIUsage.{WritingAssistanceFunctionName}.CoreOptionType"
     enum="WritingAssistanceMetricsOptionType" expires_after="2025-10-27">
diff --git a/tools/metrics/histograms/metadata/blink/enums.xml b/tools/metrics/histograms/metadata/blink/enums.xml
index 35778ec..f2783d8 100644
--- a/tools/metrics/histograms/metadata/blink/enums.xml
+++ b/tools/metrics/histograms/metadata/blink/enums.xml
@@ -7806,6 +7806,17 @@
 
 <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom:CSSSampleId) -->
 
+<!-- LINT.IfChange(OriginStatusOnSubresource) -->
+
+<enum name="OriginStatusOnSubresource">
+  <int value="0" label="kFromSameOriginToSameOrigin"/>
+  <int value="1" label="kFromSameOriginToCrossOrigin"/>
+  <int value="2" label="kFromCrossOriginToSameOrigin"/>
+  <int value="3" label="kFromCrossOriginToCrossOrigin"/>
+</enum>
+
+<!-- LINT.ThenChange(//third_party/blink/renderer/core/loader/preload_helper.h:OriginStatusOnSubresource) -->
+
 <!-- LINT.IfChange(PermissionElementInvalidStyleReason) -->
 
 <enum name="PermissionElementInvalidStyleReason">
diff --git a/tools/metrics/histograms/metadata/browser/enums.xml b/tools/metrics/histograms/metadata/browser/enums.xml
index c2d90438..e170e3c 100644
--- a/tools/metrics/histograms/metadata/browser/enums.xml
+++ b/tools/metrics/histograms/metadata/browser/enums.xml
@@ -298,7 +298,8 @@
   <int value="95" label="SMS_RECEIVER_INFOBAR_DELEGATE"/>
   <int value="96" label="KNOWN_INTERCEPTION_DISCLOSURE_INFOBAR_DELEGATE"/>
   <int value="97" label="SYNC_ERROR_INFOBAR_DELEGATE_ANDROID"/>
-  <int value="98" label="MIXED_CONTENT_DOWNLOAD_INFOBAR_DELEGATE_ANDROID"/>
+  <int value="98"
+      label="MIXED_CONTENT_DOWNLOAD_INFOBAR_DELEGATE_ANDROID (Obsolete)"/>
   <int value="99" label="CONDITIONAL_TAB_STRIP_INFOBAR_ANDROID"/>
   <int value="100" label="LITE_MODE_HTTPS_IMAGE_COMPRESSION_INFOBAR_ANDROID"/>
   <int value="101" label="SYSTEM_INFOBAR_DELEGATE_MAC (Obsolete)"/>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/enums.xml b/tools/metrics/histograms/metadata/custom_tabs/enums.xml
index fb9f2f565..f2168d4 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/enums.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/enums.xml
@@ -312,6 +312,21 @@
       label="Open a history navigation popup after long pressing button."/>
 </enum>
 
+<!-- LINT.IfChange(WebAppHeaderDisplayMode) -->
+
+<enum name="WebAppHeaderDisplayMode">
+  <int value="0" label="browser"/>
+  <int value="1" label="minimalUI"/>
+  <int value="2" label="standalone"/>
+  <int value="3" label="fullscreen"/>
+  <int value="4" label="windowControlsOverlay"/>
+  <int value="5" label="tabbed"/>
+  <int value="6" label="borderless"/>
+  <int value="7" label="pictureInPicture"/>
+</enum>
+
+<!-- LINT.ThenChange(//third_party/blink/public/mojom/manifest/display_mode.mojom:DisplayMode)-->
+
 <enum name="WebAppReloadButtonEvent">
   <int value="0"
       label="Invalid reload type due to tab switching, e.g. re-parenting."/>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index 4f51473..6e674ea 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -45,6 +45,14 @@
   </variant>
 </variants>
 
+<variants name="StartupSuffix">
+  <variant name="Cold" summary=""/>
+  <variant name="Speculated"
+      summary="When the mayLaunchUrl speculation is used"/>
+  <variant name="Warm" summary="When CustomTabsConnection#warmUp is not used"/>
+  <variant name="WarmedUp" summary="When CustomTabsConnection#warmUp is used"/>
+</variants>
+
 <histogram name="CustomTab.SessionDuration{CustomTabOpenSource}" units="ms"
     expires_after="2020-10-18">
   <owner>ranj@chromium.org</owner>
@@ -773,19 +781,7 @@
 
     {suffix}.
   </summary>
-  <token key="suffix">
-    <variant name="Cold"
-        summary="Measures from process creation to first commit"/>
-    <variant name="Speculated"
-        summary="Measures from intent received to first commit when the
-                 mayLaunchUrl speculation is used"/>
-    <variant name="Warm"
-        summary="Measures from intent received to first commit when
-                 CustomTabsConnection#warmUp is not used"/>
-    <variant name="WarmedUp"
-        summary="Measures from intent received to first commit when
-                 CustomTabsConnection#warmUp is used"/>
-  </token>
+  <token key="suffix" variants="StartupSuffix"/>
 </histogram>
 
 <histogram name="CustomTabs.Startup.TimeToFirstContentfulPaint.{suffix}"
@@ -806,18 +802,7 @@
 
     {suffix}.
   </summary>
-  <token key="suffix">
-    <variant name="Cold" summary="Measures from process creation to LCP"/>
-    <variant name="Speculated"
-        summary="Measures from intent received to LCP when the mayLaunchUrl
-                 speculation is used"/>
-    <variant name="Warm"
-        summary="Measures from intent received to LCP when
-                 CustomTabsConnection#warmUp is not used"/>
-    <variant name="WarmedUp"
-        summary="Measures from intent received to LCP when
-                 CustomTabsConnection#warmUp is used"/>
-  </token>
+  <token key="suffix" variants="StartupSuffix"/>
 </histogram>
 
 <histogram name="CustomTabs.Startup.TimeToLargestContentfulPaint2.{suffix}"
@@ -838,18 +823,7 @@
 
     {suffix}.
   </summary>
-  <token key="suffix">
-    <variant name="Cold" summary="Measures from process creation to LCP"/>
-    <variant name="Speculated"
-        summary="Measures from intent received to LCP when the mayLaunchUrl
-                 speculation is used"/>
-    <variant name="Warm"
-        summary="Measures from intent received to LCP when
-                 CustomTabsConnection#warmUp is not used"/>
-    <variant name="WarmedUp"
-        summary="Measures from intent received to LCP when
-                 CustomTabsConnection#warmUp is used"/>
-  </token>
+  <token key="suffix" variants="StartupSuffix"/>
 </histogram>
 
 <histogram name="CustomTabs.TimeElapsedSinceMinimized.Destroyed" units="s"
@@ -914,6 +888,36 @@
   </summary>
 </histogram>
 
+<histogram name="CustomTabs.WebAppHeader.ControlsHiddenTime" units="seconds"
+    expires_after="2026-05-31">
+  <owner>japhet@chromium.org</owner>
+  <owner>pwa-team@google.com</owner>
+  <summary>
+    Records how long PWA controls are hidden due to the window size being too
+    small. Recorded when transitioning hidden to shown, and on destruction.
+  </summary>
+</histogram>
+
+<histogram name="CustomTabs.WebAppHeader.ControlsShownTime" units="seconds"
+    expires_after="2026-05-31">
+  <owner>japhet@chromium.org</owner>
+  <owner>pwa-team@google.com</owner>
+  <summary>
+    Records how long PWA controls are shown before being hidden due to window
+    size changes. Recorded when transitioning shown to hidden, and on
+    destruction.
+  </summary>
+</histogram>
+
+<histogram name="CustomTabs.WebAppHeader.DisplayMode"
+    enum="WebAppHeaderDisplayMode" expires_after="2026-05-31">
+  <owner>japhet@chromium.org</owner>
+  <owner>pwa-team@google.com</owner>
+  <summary>
+    Records the DisplayMode of the the PWA header when it first becomes visible.
+  </summary>
+</histogram>
+
 <histogram name="CustomTabs.WebAppHeader.ReloadButtonEvent"
     enum="WebAppReloadButtonEvent" expires_after="2026-05-31">
   <owner>vkorotkevich@google.com</owner>
@@ -1003,6 +1007,54 @@
   </summary>
 </histogram>
 
+<histogram
+    name="TrustedWebActivity.Startup.TimeToFirstCommitNavigation2.{suffix}"
+    units="ms" expires_after="2026-05-13">
+  <owner>hattorij@google.com</owner>
+  <owner>ishitatsuyuki@google.com</owner>
+  <owner>cros-virt-devices-guests@google.com</owner>
+  <summary>
+    Duration for a TWA from the launch of the TWA to the first navigation
+    commit. Split by varying degrees of warmness, from cold to fully pre-loaded
+    through mayLaunchUrl.
+
+    {suffix}.
+  </summary>
+  <token key="suffix" variants="StartupSuffix"/>
+</histogram>
+
+<histogram
+    name="TrustedWebActivity.Startup.TimeToFirstContentfulPaint.{suffix}"
+    units="ms" expires_after="2026-05-13">
+  <owner>hattorij@google.com</owner>
+  <owner>ishitatsuyuki@google.com</owner>
+  <owner>cros-virt-devices-guests@google.com</owner>
+  <summary>
+    Duration for a TWA from the launch to the First Contentful Paint on the
+    first navigation. Split by varying degrees of warmness, from cold to fully
+    pre-loaded through mayLaunchUrl.
+
+    {suffix}.
+  </summary>
+  <token key="suffix" variants="StartupSuffix"/>
+</histogram>
+
+<histogram
+    name="TrustedWebActivity.Startup.TimeToLargestContentfulPaint2.{suffix}"
+    units="ms" expires_after="2026-05-13">
+  <owner>hattorij@google.com</owner>
+  <owner>ishitatsuyuki@google.com</owner>
+  <owner>cros-virt-devices-guests@google.com</owner>
+  <summary>
+    Duration for a TWA from the launch to the Largest Contentful Paint on the
+    first navigation. Split by varying degrees of warmness, from cold to fully
+    pre-loaded through mayLaunchUrl.
+
+    {suffix}.
+  </summary>
+  <token key="suffix" variants="StartupSuffix"/>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/enterprise/enums.xml b/tools/metrics/histograms/metadata/enterprise/enums.xml
index b56013d..79ff9ea 100644
--- a/tools/metrics/histograms/metadata/enterprise/enums.xml
+++ b/tools/metrics/histograms/metadata/enterprise/enums.xml
@@ -2231,6 +2231,7 @@
   <int value="1360" label="NTPFooterExtensionAttributionEnabled"/>
   <int value="1361" label="ClearWindowNameForNewBrowsingContextGroup"/>
   <int value="1362" label="PasswordManagerBlocklist"/>
+  <int value="1363" label="TLS13EarlyDataEnabled"/>
 </enum>
 
 <enum name="EnterprisePoliciesSources">
diff --git a/tools/metrics/histograms/metadata/ios/enums.xml b/tools/metrics/histograms/metadata/ios/enums.xml
index bbe7af5..446b9f5 100644
--- a/tools/metrics/histograms/metadata/ios/enums.xml
+++ b/tools/metrics/histograms/metadata/ios/enums.xml
@@ -650,6 +650,8 @@
   <int value="16" label="Tips (with Product Image)"/>
   <int value="17" label="Tips"/>
   <int value="18" label="Send Tab to Self Promo"/>
+  <int value="19" label="Obsolete - Set Up List Docking"/>
+  <int value="20" label="Obsolete - Set Up List Address Bar"/>
   <int value="21" label="ShopCard"/>
 </enum>
 
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index a35ec01d..0acfee3 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -1538,8 +1538,8 @@
   <summary>
     This records how many times a request was deferred due to a Device Bound
     Session (https://github.com/w3c/webappsec-dbsc/blob/main/README.md). This is
-    logged once for most network requests that can add a cookie header; requests
-    that attempt to refresh but fail are omitted.
+    logged once for each round trip of a network requests that can add a cookie
+    header; requests that attempt to refresh but fail are omitted.
   </summary>
 </histogram>
 
@@ -1550,8 +1550,9 @@
   <summary>
     This records whether a request was deferred due to a Device Bound Session
     (https://github.com/w3c/webappsec-dbsc/blob/main/README.md) and the reason
-    for that decision. This is logged once for most network requests that can
-    add a cookie header; requests that attempt to refresh but fail are omitted.
+    for that decision. This is logged once for each round trip of most network
+    requests that can add a cookie header; requests that attempt to refresh but
+    fail are omitted.
   </summary>
 </histogram>
 
@@ -1562,9 +1563,9 @@
   <summary>
     This records the duration of each time a request is deferred due to a Device
     Bound Session (https://github.com/w3c/webappsec-dbsc/blob/main/README.md).
-    This is logged every time a network request is deferred. Under normal
-    operations, a small fraction of network requests are deferred at most once.
-    See also Net.DeviceBoundSessions.TotalRequestDeferredDuration.
+    This is logged every time one round trip of a network request is deferred.
+    Under normal operations, a small fraction of network requests are deferred
+    at most once. See also Net.DeviceBoundSessions.TotalRequestDeferredDuration.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/structured_metrics/OWNERS b/tools/metrics/histograms/metadata/structured_metrics/OWNERS
index 6a6720d..8c5f9430 100644
--- a/tools/metrics/histograms/metadata/structured_metrics/OWNERS
+++ b/tools/metrics/histograms/metadata/structured_metrics/OWNERS
@@ -1,5 +1,3 @@
 per-file OWNERS=file://tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
 
-# Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
-jongahn@chromium.org
diff --git a/tools/metrics/histograms/metadata/structured_metrics/histograms.xml b/tools/metrics/histograms/metadata/structured_metrics/histograms.xml
index 6dd79ea9..511f9e4 100644
--- a/tools/metrics/histograms/metadata/structured_metrics/histograms.xml
+++ b/tools/metrics/histograms/metadata/structured_metrics/histograms.xml
@@ -11,7 +11,7 @@
 <histogram name="StructuredMetrics.ExternalMetricsDropped" units="count"
     expires_after="2025-10-19">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -24,7 +24,7 @@
 <histogram name="StructuredMetrics.ExternalMetricsDropped2.{Project}"
     units="count" expires_after="2025-10-26">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     The number of events that are dropped when processing External Metrics per
@@ -55,7 +55,7 @@
 <histogram name="StructuredMetrics.ExternalMetricsProduced2.{Project}"
     units="count" expires_after="2025-10-26">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     The number of events that are produced by Platform2 per-project. This field
@@ -85,7 +85,7 @@
 <histogram name="StructuredMetrics.FlushedMap.Error" units="FlushedMapError"
     expires_after="2025-05-21">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     Logs errors that occur in FlushedMap. This metric is captured when events
@@ -96,7 +96,7 @@
 <histogram name="StructuredMetrics.FlushedMap.LoadedFlushedKeys" units="count"
     expires_after="2025-05-21">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     The number of flushed keys loaded from the previous session. This is
@@ -107,7 +107,7 @@
 <histogram name="StructuredMetrics.InitSequence" enum="UmaInitSequence"
     expires_after="2025-07-14">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -122,7 +122,7 @@
 <histogram name="StructuredMetrics.LogStore.CompressionRatio" units="%"
     expires_after="2025-11-16">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -134,7 +134,7 @@
 <histogram name="StructuredMetrics.LogStore.Dropped" units="logs"
     expires_after="2025-07-14">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -147,7 +147,7 @@
 <histogram name="StructuredMetrics.LogStore.DroppedSize" units="KB"
     expires_after="2025-07-14">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -160,7 +160,7 @@
 <histogram name="StructuredMetrics.OnExternalMetricsCollectedDuration"
     units="microseconds" expires_after="2025-03-20">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     Messures the duration of
@@ -173,7 +173,7 @@
 <histogram name="StructuredMetrics.Reporting.ActualUploadInterval"
     units="minutes" expires_after="2025-03-30">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -186,7 +186,7 @@
 <histogram name="StructuredMetrics.Reporting.HTTPErrorCode"
     enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-05-25">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -198,7 +198,7 @@
 <histogram name="StructuredMetrics.Reporting.HTTPResponseCode"
     enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-05-25">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -210,7 +210,7 @@
 <histogram name="StructuredMetrics.Reporting.LogSize.OnSuccess" units="KB"
     expires_after="2025-03-30">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -222,7 +222,7 @@
 <histogram name="StructuredMetrics.Reporting.LogSize.RejectedSize" units="KB"
     expires_after="2025-07-14">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
@@ -233,7 +233,7 @@
 <histogram name="StructuredMetrics.StorageManager.DeletedBufferWhenOverQuota"
     units="buffers" expires_after="2025-05-21">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     The number of flushed event buffers that were deleted when our disk quota
@@ -244,7 +244,7 @@
 <histogram name="StructuredMetrics.StorageManager.DiskQuotaExceededDelta"
     units="KB" expires_after="2025-09-14">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     The number of Kibibytes the disk quota was exceeded by when buffers were
@@ -257,7 +257,7 @@
 <histogram name="StructuredMetrics.StorageManager.Flush"
     units="StorageManagerFlushStatus" expires_after="2025-09-28">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     The result of flushing an in-memory event buffer to disk. Captures success
@@ -268,7 +268,7 @@
 <histogram name="StructuredMetrics.StorageManager.FlushedBuffersAtUpload"
     units="buffers" expires_after="2025-05-21">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     The number of flushed buffers when an upload occurs. Currently, all flushed
@@ -282,7 +282,7 @@
 <histogram name="StructuredMetrics.StorageManager.InMemoryEventsAtUpload"
     units="events" expires_after="2025-05-21">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     The number of in-memory events read when an upload occurs. This is recorded
@@ -293,7 +293,7 @@
 <histogram name="StructuredMetrics.StorageManager.MaxDiskDizeKB" units="KB"
     expires_after="2025-05-21">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     The max amount of disk space flushed events can consume. This is recorded
@@ -304,7 +304,7 @@
 <histogram name="StructuredMetrics.StorageManager.MaxMemorySizeKB" units="KB"
     expires_after="2025-05-21">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     The max amount of memory in-memory events can consume. This is recorded when
@@ -315,7 +315,7 @@
 <histogram name="StructuredMetrics.StorageManager.RecordStatus"
     enum="RecordStatus" expires_after="2025-09-28">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <summary>
     Records the status of an event recording. This is recorded when an event is
@@ -326,7 +326,7 @@
 <histogram name="StructuredMetrics.UploadSize" units="bytes"
     expires_after="2025-09-28">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@google.com</owner>
+  <owner>troywang@google.com</owner>
   <owner>chromeos-data-eng@google.com</owner>
   <owner>chrome-metrics-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/uma/histograms.xml b/tools/metrics/histograms/metadata/uma/histograms.xml
index f9e0ed1..3510b32 100644
--- a/tools/metrics/histograms/metadata/uma/histograms.xml
+++ b/tools/metrics/histograms/metadata/uma/histograms.xml
@@ -216,7 +216,7 @@
 
 <histogram name="UMA.CrosPerUser.DaemonStoreWriteFailed"
     enum="PerUserDaemonStoreFail" expires_after="2023-04-16">
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
     Records the status of writing the per-user metrics state to daemon-store (or
@@ -227,7 +227,7 @@
 
 <histogram name="UMA.CrosPerUser.IdReset" enum="PerUserIdType"
     expires_after="2024-02-04">
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <owner>asvitkine@chromium.org</owner>
   <summary>
     Counts the number of times an ID was reset. The client ID is reset when a
@@ -239,7 +239,7 @@
 
 <histogram name="UMA.CrosPerUser.UserLogStoreState" enum="PerUserLogStoreState"
     expires_after="2023-04-16">
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <owner>asvitkine@chromium.org</owner>
   <summary>
     Records the state that metrics service is in when user log store was set or
@@ -823,7 +823,7 @@
 <histogram name="UMA.StructuredMetrics.EventFileSize" units="KB"
     expires_after="2024-02-01">
   <owner>tby@chromium.org</owner>
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <summary>
     File size of events recorded outside of Chrome. This metric is recorded for
     every file processed by an external metrics scan, which reads the events
@@ -834,7 +834,7 @@
 
 <histogram name="UMA.StructuredMetrics.EventRecordingState2"
     enum="StructuredMetricsEventRecordingState2" expires_after="2024-02-01">
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <owner>tby@chromium.org</owner>
   <owner>rkaplow@chromium.org</owner>
   <owner>asvitkine@chromium.org</owner>
@@ -848,7 +848,7 @@
 <histogram name="UMA.StructuredMetrics.EventSerializedSize" units="bytes"
     expires_after="2024-04-28">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <summary>
     The serialized size in bytes of a Structured Metrics event. This is the size
     the event uses when serialized to be uploaded.
@@ -857,7 +857,7 @@
 
 <histogram name="UMA.StructuredMetrics.EventsRecordedBeforeInit" units="scans"
     expires_after="2024-04-28">
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <owner>tby@chromium.org</owner>
   <summary>
     Number of events recorded before private keys are initialized to hash
@@ -868,7 +868,7 @@
 <histogram name="UMA.StructuredMetrics.ExternalMetricScansPerUpload"
     units="count" expires_after="2024-04-28">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <summary>
     The number of external metric scans from platform2 were performed since the
     last Structured Metric upload. An external metric scan collects events that
@@ -879,7 +879,7 @@
 
 <histogram name="UMA.StructuredMetrics.InternalError2"
     enum="StructuredMetricsInternalError2" expires_after="2025-11-16">
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <owner>tby@chromium.org</owner>
   <owner>rkaplow@chromium.org</owner>
   <owner>asvitkine@chromium.org</owner>
@@ -891,7 +891,7 @@
 
 <histogram name="UMA.StructuredMetrics.KeyValidationState"
     enum="StructuredMetricsKeyValidationState" expires_after="2024-02-01">
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <owner>tby@chromium.org</owner>
   <owner>rkaplow@chromium.org</owner>
   <owner>asvitkine@chromium.org</owner>
@@ -907,7 +907,7 @@
 
 <histogram name="UMA.StructuredMetrics.NumEventsInUpload" units="count"
     expires_after="2025-11-16">
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <owner>tby@chromium.org</owner>
   <owner>rkaplow@chromium.org</owner>
   <owner>asvitkine@chromium.org</owner>
@@ -920,7 +920,7 @@
 <histogram name="UMA.StructuredMetrics.NumFilesPerExternalMetricsScan"
     units="count" expires_after="2025-11-16">
   <owner>tby@chromium.org</owner>
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <summary>
     How many files in the external Structured metrics directory before an
     external metrics scan is triggered. The external metrics scan will read the
@@ -931,7 +931,7 @@
 <histogram name="UMA.StructuredMetrics.UploadSize" units="bytes"
     expires_after="2024-02-01">
   <owner>andrewbregger@google.com</owner>
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <owner>tby@chromium.org</owner>
   <summary>
     The serialized size in bytes of the Structured Metrics payload attached to
diff --git a/tools/metrics/structured/OWNERS b/tools/metrics/structured/OWNERS
index 350d5e6e..135f833 100644
--- a/tools/metrics/structured/OWNERS
+++ b/tools/metrics/structured/OWNERS
@@ -1,4 +1,4 @@
-jongahn@chromium.org
+troywang@google.com
 
 # Last resort reviewers
 andrewbregger@google.com
diff --git a/tools/metrics/structured/sync/structured.xml b/tools/metrics/structured/sync/structured.xml
index 3350c2d6..a7a86dc 100644
--- a/tools/metrics/structured/sync/structured.xml
+++ b/tools/metrics/structured/sync/structured.xml
@@ -964,7 +964,7 @@
 </project>
 
 <project name="StructuredMetrics">
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <owner>tby@chromium.org</owner>
   <id>per-project</id>
   <scope>device</scope>
@@ -990,7 +990,7 @@
 </project>
 
 <project name="CrOSEvents" cros_events="true" targets="webui,chromium">
-  <owner>jongahn@chromium.org</owner>
+  <owner>troywang@google.com</owner>
   <id>per-project</id>
   <scope>device</scope>
   <key-rotation>120</key-rotation>
@@ -3668,4 +3668,4 @@
   </event>
 </project>
 
-</structured-metrics>
\ No newline at end of file
+</structured-metrics>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 3949bb7..95e6e515 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -4848,6 +4848,23 @@
   </metric>
 </event>
 
+<event name="Blink.Preloading.ByLinkHeader">
+  <owner>tnak@chromium.org</owner>
+  <owner>chrome-loading@google.com</owner>
+  <summary>
+    Events taken when HTTP responses are received and they have Link headers.
+    Multiple events should be involved if one response contains multiple Link
+    headers. This event provides insights into potential information leakage
+    related to Link headers on subresources.
+  </summary>
+  <metric name="OriginStatusOnSubresource" enum="OriginStatusOnSubresource">
+    <summary>
+      Records the cross-origin usage related to Link headers on subresource
+      fetches. This is only recorded for subresource fetches.
+    </summary>
+  </metric>
+</event>
+
 <event name="Blink.Script.AsyncScripts" singular="True">
   <owner>dom@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 6bc08ef..e975052 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@
         },
         "win": {
             "hash": "723c1dafd0ca7b7c01ac77a49bec9ede038399a0",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/0a066d12ea83708e99be5859f3de2f48d06e2c21/trace_processor_shell.exe"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/188242925b83331305ff6f8f146b2a6667f21ac2/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v50.1/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "e58b0fd4afa1f510cc2775f3f688bd13f1c9a658",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/342080f33a28c65850ef2e15df7618869502a273/trace_processor_shell"
+            "hash": "755c27d1aaeb0956ec65970336865b7748664076",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/410e8a174f36c6293d3aebd3833d04ac3f367b40/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/utr/run.py b/tools/utr/run.py
index 9d1c56b..5a0ec18 100755
--- a/tools/utr/run.py
+++ b/tools/utr/run.py
@@ -12,7 +12,8 @@
 
 - vpython3 run.py -B $BUCKET -b $BUILDER -t $TEST compile
 - vpython3 run.py -B $BUCKET -b $BUILDER -t $TEST compile-and-test
-- vpython3 run.py -B $BUCKET -b $BUILDER -t $TEST test --gtest_filter=Test.Case
+- vpython3 run.py -B $BUCKET -b $BUILDER -t $TEST test --
+    --gtest_filter=Test.Case
 """
 
 import argparse
diff --git a/ui/gfx/canvas.cc b/ui/gfx/canvas.cc
index 8766c034..2c7dfd1 100644
--- a/ui/gfx/canvas.cc
+++ b/ui/gfx/canvas.cc
@@ -523,7 +523,22 @@
   shader_scale.setScale(SkFloatToScalar(user_scale_x),
                         SkFloatToScalar(user_scale_y));
   shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y));
+#if BUILDFLAG(IS_CHROMEOS)
+  // In non pixel-canvas mode, the scaling and rounding is performed in cc side.
+  // In pixel canvas mode, we need to translate so that the position is pixel
+  // aligned at the target space, because drawing at subpixel position can
+  // result in pixelated image. Use floor so that kRepeat will not start at the
+  // end.
+  // TOOD(crbug.com/41344902): Move the pixel canvas feature flag to
+  // ui/base and use it instead BUILDFLAG.
+  // TOOD(crbug.com/41344902): Using image_scale_ isn't 100% accurate. It should
+  // use the scale applied to the canvas instead (which isn't available now).
+  shader_scale.postTranslate(
+      SkFloatToScalar(std::floor(dest_x * image_scale_) / image_scale_),
+      SkFloatToScalar(std::floor(dest_y * image_scale_) / image_scale_));
+#else
   shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y));
+#endif
 
   cc::PaintFlags flags(original_flags);
   flags.setFilterQuality(filter ? cc::PaintFlags::FilterQuality::kLow
diff --git a/ui/gfx/geometry/BUILD.gn b/ui/gfx/geometry/BUILD.gn
index 239b40f..98aeed5 100644
--- a/ui/gfx/geometry/BUILD.gn
+++ b/ui/gfx/geometry/BUILD.gn
@@ -22,7 +22,9 @@
     "insets_conversions.h",
     "insets_f.cc",
     "insets_f.h",
+    "insets_outsets_base.cc",
     "insets_outsets_base.h",
+    "insets_outsets_f_base.cc",
     "insets_outsets_f_base.h",
     "matrix3_f.cc",
     "matrix3_f.h",
diff --git a/ui/gfx/geometry/insets_outsets_base.cc b/ui/gfx/geometry/insets_outsets_base.cc
new file mode 100644
index 0000000..27099622
--- /dev/null
+++ b/ui/gfx/geometry/insets_outsets_base.cc
@@ -0,0 +1,23 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/insets_outsets_base.h"
+
+#include <string>
+
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/outsets.h"
+
+namespace gfx {
+
+template <typename T>
+std::string InsetsOutsetsBase<T>::ToString() const {
+  return base::StringPrintf("x:%g,%g y:%g,%g", left_, right_, top_, bottom_);
+}
+
+template class InsetsOutsetsBase<Insets>;
+template class InsetsOutsetsBase<Outsets>;
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/insets_outsets_base.h b/ui/gfx/geometry/insets_outsets_base.h
index a0d3364..4ca1286 100644
--- a/ui/gfx/geometry/insets_outsets_base.h
+++ b/ui/gfx/geometry/insets_outsets_base.h
@@ -7,8 +7,8 @@
 
 #include <string>
 
+#include "base/component_export.h"
 #include "base/numerics/clamped_math.h"
-#include "base/strings/stringprintf.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace gfx {
@@ -133,9 +133,7 @@
   }
 
   // Returns a string representation of the insets/outsets.
-  std::string ToString() const {
-    return base::StringPrintf("x:%d,%d y:%d,%d", left_, right_, top_, bottom_);
-  }
+  COMPONENT_EXPORT(GEOMETRY) std::string ToString() const;
 
  private:
   // Clamp the bottom/right to avoid integer over/underflow in width() and
diff --git a/ui/gfx/geometry/insets_outsets_f_base.cc b/ui/gfx/geometry/insets_outsets_f_base.cc
new file mode 100644
index 0000000..6aa6fe26
--- /dev/null
+++ b/ui/gfx/geometry/insets_outsets_f_base.cc
@@ -0,0 +1,23 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/insets_outsets_f_base.h"
+
+#include <string>
+
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/outsets_f.h"
+
+namespace gfx {
+
+template <typename T>
+std::string InsetsOutsetsFBase<T>::ToString() const {
+  return base::StringPrintf("x:%g,%g y:%g,%g", left_, right_, top_, bottom_);
+}
+
+template class InsetsOutsetsFBase<InsetsF>;
+template class InsetsOutsetsFBase<OutsetsF>;
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/insets_outsets_f_base.h b/ui/gfx/geometry/insets_outsets_f_base.h
index 8d6b1d5c..40fee5e5 100644
--- a/ui/gfx/geometry/insets_outsets_f_base.h
+++ b/ui/gfx/geometry/insets_outsets_f_base.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "base/strings/stringprintf.h"
+#include "base/component_export.h"
 
 namespace gfx {
 
@@ -113,9 +113,7 @@
   }
 
   // Returns a string representation of the insets/outsets.
-  std::string ToString() const {
-    return base::StringPrintf("x:%g,%g y:%g,%g", left_, right_, top_, bottom_);
-  }
+  COMPONENT_EXPORT(GEOMETRY) std::string ToString() const;
 
  private:
   float top_ = 0.f;
diff --git a/ui/gfx/geometry/outsets.h b/ui/gfx/geometry/outsets.h
index 56207e6..22a7036 100644
--- a/ui/gfx/geometry/outsets.h
+++ b/ui/gfx/geometry/outsets.h
@@ -5,6 +5,7 @@
 #ifndef UI_GFX_GEOMETRY_OUTSETS_H_
 #define UI_GFX_GEOMETRY_OUTSETS_H_
 
+#include "base/component_export.h"
 #include "base/numerics/clamped_math.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/insets_outsets_base.h"
@@ -13,7 +14,7 @@
 
 // This can be used to represent a space surrounding a rectangle, by
 // "expanding" the rectangle by the outset amount on all four sides.
-class Outsets : public InsetsOutsetsBase<Outsets> {
+class COMPONENT_EXPORT(GEOMETRY) Outsets : public InsetsOutsetsBase<Outsets> {
  public:
   using InsetsOutsetsBase::InsetsOutsetsBase;
 
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index e979942..e3d0ef8 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -198,12 +198,10 @@
 
   if (use_ozone) {
     if (!is_fuchsia) {
-      if (!use_static_angle) {
-        data_deps += [
-          "//third_party/angle:libEGL",
-          "//third_party/angle:libGLESv2",
-        ]
-      }
+      data_deps += [
+        "//third_party/angle:libEGL",
+        "//third_party/angle:libGLESv2",
+      ]
       if (angle_shared_libvulkan && !is_chromeos) {
         data_deps += [ "//third_party/vulkan-loader/src:libvulkan" ]
       }
@@ -225,12 +223,10 @@
       "//ui/gfx/linux:gpu_memory_buffer_support_x11",
     ]
 
-    if (!use_static_angle) {
-      data_deps += [
-        "//third_party/angle:libEGL",
-        "//third_party/angle:libGLESv2",
-      ]
-    }
+    data_deps += [
+      "//third_party/angle:libEGL",
+      "//third_party/angle:libGLESv2",
+    ]
     if (angle_shared_libvulkan && !is_chromeos) {
       data_deps += [ "//third_party/vulkan-loader/src:libvulkan" ]
     }
@@ -294,15 +290,11 @@
     ]
 
     data_deps += [
+      "//third_party/angle:libEGL",
+      "//third_party/angle:libGLESv2",
       "//third_party/swiftshader/src/Vulkan:icd_file",
       "//third_party/swiftshader/src/Vulkan:swiftshader_libvulkan",
     ]
-    if (!use_static_angle) {
-      data_deps += [
-        "//third_party/angle:libEGL",
-        "//third_party/angle:libGLESv2",
-      ]
-    }
     if (angle_shared_libvulkan && !is_chromeos) {
       data_deps += [ "//third_party/vulkan-loader/src:libvulkan" ]
     }
@@ -335,13 +327,11 @@
       "Quartz.framework",
     ]
 
-    data_deps += [ "//third_party/mesa_headers" ]
-    if (!use_static_angle) {
-      data_deps += [
-        "//third_party/angle:libEGL",
-        "//third_party/angle:libGLESv2",
-      ]
-    }
+    data_deps += [
+      "//third_party/angle:libEGL",
+      "//third_party/angle:libGLESv2",
+      "//third_party/mesa_headers",
+    ]
     if (enable_swiftshader) {
       data_deps += [
         "//third_party/swiftshader/src/Vulkan:icd_file",
@@ -408,18 +398,16 @@
   # "shared_library" target types it will try to link things into
   # Chromium Framework when we want to keep the ANGLE and SwiftShader
   # libraries separate instead.
-  if (!use_static_angle) {
-    copy("angle_library_copy") {
-      sources = [
-        "$root_out_dir/libEGL.dylib",
-        "$root_out_dir/libGLESv2.dylib",
-      ]
-      outputs = [ "$root_out_dir/egl_intermediates/{{source_file_part}}" ]
-      deps = [
-        "//third_party/angle:libEGL",
-        "//third_party/angle:libGLESv2",
-      ]
-    }
+  copy("angle_library_copy") {
+    sources = [
+      "$root_out_dir/libEGL.dylib",
+      "$root_out_dir/libGLESv2.dylib",
+    ]
+    outputs = [ "$root_out_dir/egl_intermediates/{{source_file_part}}" ]
+    deps = [
+      "//third_party/angle:libEGL",
+      "//third_party/angle:libGLESv2",
+    ]
   }
 
   if (enable_swiftshader) {
diff --git a/ui/gl/features.gni b/ui/gl/features.gni
index fe0da761..84678cb 100644
--- a/ui/gl/features.gni
+++ b/ui/gl/features.gni
@@ -7,7 +7,7 @@
 
 declare_args() {
   # Should ANGLE be linked statically?
-  use_static_angle = !is_mac
+  use_static_angle = is_android || is_ios
 
   # Should Dawn support be compiled to back the WebGPU implementation?
   # Also controls linking Dawn dependencies in such as SPIRV-Tools/SPIRV-Cross.
diff --git a/ui/gl/gl_surface_egl_unittest.cc b/ui/gl/gl_surface_egl_unittest.cc
index bc9d505..8fa4ff3 100644
--- a/ui/gl/gl_surface_egl_unittest.cc
+++ b/ui/gl/gl_surface_egl_unittest.cc
@@ -26,8 +26,13 @@
 class GLSurfaceEGLTest : public testing::Test {
  protected:
   void SetUp() override {
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
     display_ = GLSurfaceTestSupport::InitializeOneOffImplementation(
         GLImplementationParts(kGLImplementationEGLANGLE));
+#else
+    display_ = GLSurfaceTestSupport::InitializeOneOffImplementation(
+        GLImplementationParts(kGLImplementationEGLGLES2));
+#endif
   }
 
   void TearDown() override { GLSurfaceTestSupport::ShutdownGL(display_); }
diff --git a/ui/gl/gpu_timing.h b/ui/gl/gpu_timing.h
index 00ce9d6..9018f44 100644
--- a/ui/gl/gpu_timing.h
+++ b/ui/gl/gpu_timing.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include <memory>
-#include <queue>
 
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
diff --git a/ui/gl/init/BUILD.gn b/ui/gl/init/BUILD.gn
index 8ecd6bd..33d87da 100644
--- a/ui/gl/init/BUILD.gn
+++ b/ui/gl/init/BUILD.gn
@@ -37,7 +37,7 @@
 
   if (use_static_angle) {
     sources += [ "gl_initializer_angle.cc" ]
-    deps += [ "//third_party/angle:libGLESv2_thin_static" ]
+    deps += [ "//third_party/angle:libEGL_static" ]
   }
 
   if (is_android) {
diff --git a/ui/gl/init/gl_initializer_angle.cc b/ui/gl/init/gl_initializer_angle.cc
index 5ede331..20ee2b6 100644
--- a/ui/gl/init/gl_initializer_angle.cc
+++ b/ui/gl/init/gl_initializer_angle.cc
@@ -6,17 +6,14 @@
 
 #include <EGL/egl.h>
 
-extern "C" {
-// The ANGLE internal eglGetProcAddress
-EGLAPI __eglMustCastToProperFunctionPointerType EGLAPIENTRY
-EGL_GetProcAddress(const char* procname);
-}
-
 namespace gl {
 namespace init {
 
 bool InitializeStaticANGLEEGL() {
-  SetGLGetProcAddressProc(&EGL_GetProcAddress);
+#pragma push_macro("eglGetProcAddress")
+#undef eglGetProcAddress
+  SetGLGetProcAddressProc(&eglGetProcAddress);
+#pragma pop_macro("eglGetProcAddress")
   return true;
 }
 
diff --git a/ui/gl/init/gl_initializer_mac.cc b/ui/gl/init/gl_initializer_mac.cc
index 7bfb412..766bf7a 100644
--- a/ui/gl/init/gl_initializer_mac.cc
+++ b/ui/gl/init/gl_initializer_mac.cc
@@ -32,7 +32,11 @@
 const char kGLESv2ANGLELibraryName[] = "libGLESv2.dylib";
 const char kEGLANGLELibraryName[] = "libEGL.dylib";
 
-bool InitializeStaticEGLInternalFromLibrary() {
+bool InitializeStaticEGLInternalFromLibrary(GLImplementation implementation) {
+#if BUILDFLAG(USE_STATIC_ANGLE)
+  NOTREACHED();
+#else
+
   // Some unit test targets depend on Angle/SwiftShader but aren't built
   // as app bundles. In that case, the .dylib is next to the executable.
   base::FilePath base_dir;
@@ -85,17 +89,20 @@
   AddGLNativeLibrary(egl_library);
 
   return true;
+#endif
 }
 
 bool InitializeStaticEGLInternal(GLImplementationParts implementation) {
-  DCHECK(implementation.gl == kGLImplementationEGLANGLE);
-
 #if BUILDFLAG(USE_STATIC_ANGLE)
-  if (!InitializeStaticANGLEEGL()) {
+  if (implementation.gl == kGLImplementationEGLANGLE) {
+    // Use ANGLE if it is requested and it is statically linked
+    if (!InitializeStaticANGLEEGL())
+      return false;
+  } else if (!InitializeStaticEGLInternalFromLibrary(implementation.gl)) {
     return false;
   }
 #else
-  if (!InitializeStaticEGLInternalFromLibrary()) {
+  if (!InitializeStaticEGLInternalFromLibrary(implementation.gl)) {
     return false;
   }
 #endif  // !BUILDFLAG(USE_STATIC_ANGLE)
diff --git a/ui/gl/init/gl_initializer_win.cc b/ui/gl/init/gl_initializer_win.cc
index eabf2c3..053f113 100644
--- a/ui/gl/init/gl_initializer_win.cc
+++ b/ui/gl/init/gl_initializer_win.cc
@@ -48,7 +48,11 @@
   return true;
 }
 
-bool LoadD3DCompiler() {
+bool InitializeStaticEGLInternalFromLibrary(GLImplementation implementation) {
+#if BUILDFLAG(USE_STATIC_ANGLE)
+  NOTREACHED();
+#else
+
   base::FilePath module_path;
   if (!base::PathService::Get(base::DIR_MODULE, &module_path))
     return false;
@@ -56,15 +60,9 @@
   // Attempt to load the D3DX shader compiler using an absolute path. This is to
   // ensure that we load the versions of these DLLs that we ship. If that fails,
   // load the OS version.
-  return LoadD3DXLibrary(module_path, kD3DCompiler);
-}
+  LoadD3DXLibrary(module_path, kD3DCompiler);
 
-#if !BUILDFLAG(USE_STATIC_ANGLE)
-bool InitializeStaticEGLInternalFromLibrary() {
-  base::FilePath gles_path;
-  if (!base::PathService::Get(base::DIR_MODULE, &gles_path)) {
-    return false;
-  }
+  base::FilePath gles_path = module_path;
 
   // Load libglesv2.dll before libegl.dll because the latter is dependent on
   // the former and if there is another version of libglesv2.dll in the dll
@@ -110,22 +108,20 @@
   AddGLNativeLibrary(gles_library);
 
   return true;
+#endif
 }
-#endif  // !BUILFDLAG(USE_STATIC_ANGLE)
 
 bool InitializeStaticEGLInternal(GLImplementationParts implementation) {
-  DCHECK(implementation.gl == kGLImplementationEGLANGLE);
-
-  if (!LoadD3DCompiler()) {
-    return false;
-  }
-
 #if BUILDFLAG(USE_STATIC_ANGLE)
-  if (!InitializeStaticANGLEEGL()) {
+  if (implementation.gl == kGLImplementationEGLANGLE) {
+    // Use ANGLE if it is requested and it is statically linked
+    if (!InitializeStaticANGLEEGL())
+      return false;
+  } else if (!InitializeStaticEGLInternalFromLibrary(implementation.gl)) {
     return false;
   }
 #else
-  if (!InitializeStaticEGLInternalFromLibrary()) {
+  if (!InitializeStaticEGLInternalFromLibrary(implementation.gl)) {
     return false;
   }
 #endif  // !BUILDFLAG(USE_STATIC_ANGLE)
diff --git a/ui/ozone/common/BUILD.gn b/ui/ozone/common/BUILD.gn
index c3fa87c..7ce40f68 100644
--- a/ui/ozone/common/BUILD.gn
+++ b/ui/ozone/common/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//build/config/ozone.gni")
 import("//build/config/ui.gni")
-import("//ui/gl/features.gni")
 
 assert(use_ozone)
 
@@ -45,10 +44,6 @@
     "//ui/gl:buildflags",
   ]
 
-  if (use_static_angle) {
-    deps += [ "//third_party/angle:libGLESv2_thin_static" ]
-  }
-
   data_deps = [ "//third_party/angle:includes" ]
 
   visibility = [
diff --git a/ui/ozone/common/egl_util.cc b/ui/ozone/common/egl_util.cc
index c93aacd..c387926 100644
--- a/ui/ozone/common/egl_util.cc
+++ b/ui/ozone/common/egl_util.cc
@@ -17,13 +17,6 @@
 #include <stdlib.h>
 #endif
 
-#if BUILDFLAG(USE_STATIC_ANGLE)
-extern "C" {
-// The ANGLE internal eglGetProcAddress
-EGLAPI __eglMustCastToProperFunctionPointerType EGLAPIENTRY
-EGL_GetProcAddress(const char* procname);
-#endif  // BUILDFLAG(USE_STATIC_ANGLE)
-}
 namespace ui {
 namespace {
 
@@ -38,12 +31,10 @@
 const base::FilePath::CharType kDefaultGlesSoname[] =
     FILE_PATH_LITERAL("libGLESv2.so.2");
 #endif
-#if !BUILDFLAG(USE_STATIC_ANGLE)
 const base::FilePath::CharType kAngleEglSoname[] =
     FILE_PATH_LITERAL("libEGL.so");
 const base::FilePath::CharType kAngleGlesSoname[] =
     FILE_PATH_LITERAL("libGLESv2.so");
-#endif  // !BUILDFLAG(USE_STATIC_ANGLE)
 
 bool LoadEGLGLES2Bindings(const base::FilePath& egl_library_path,
                           const base::FilePath& gles_library_path) {
@@ -142,10 +133,6 @@
   base::FilePath egl_path;
 
   if (implementation.gl == gl::kGLImplementationEGLANGLE) {
-#if BUILDFLAG(USE_STATIC_ANGLE)
-    gl::SetGLGetProcAddressProc(&EGL_GetProcAddress);
-    return true;
-#else
     base::FilePath module_path;
 #if !BUILDFLAG(IS_FUCHSIA)
     if (!base::PathService::Get(base::DIR_MODULE, &module_path))
@@ -154,7 +141,6 @@
 
     glesv2_path = module_path.Append(kAngleGlesSoname);
     egl_path = module_path.Append(kAngleEglSoname);
-#endif  // BUILDFLAG(USE_STATIC_ANGLE)
   } else {
     glesv2_path = base::FilePath(kDefaultGlesSoname);
     egl_path = base::FilePath(kDefaultEglSoname);
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 90427eb..59f3cb17 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -9,7 +9,6 @@
 import("//build/config/ozone.gni")
 import("//gpu/vulkan/features.gni")
 import("//testing/test.gni")
-import("//ui/gl/features.gni")
 
 visibility = [ "//ui/ozone/*" ]
 
@@ -156,7 +155,7 @@
     "//ui/platform_window",
   ]
 
-  if (!is_castos && !use_static_angle) {
+  if (!is_castos) {
     data_deps = [
       "//third_party/angle:libEGL",
       "//third_party/angle:libGLESv2",
diff --git a/ui/ozone/platform/flatland/BUILD.gn b/ui/ozone/platform/flatland/BUILD.gn
index d501cab..d493bb51 100644
--- a/ui/ozone/platform/flatland/BUILD.gn
+++ b/ui/ozone/platform/flatland/BUILD.gn
@@ -5,7 +5,6 @@
 assert(is_fuchsia)
 
 import("//gpu/vulkan/features.gni")
-import("//ui/gl/features.gni")
 
 assert(enable_vulkan)
 
@@ -85,13 +84,10 @@
     "//ui/platform_window",
   ]
 
-  data_deps = []
-  if (!use_static_angle) {
-    data_deps += [
-      "//third_party/angle:libEGL",
-      "//third_party/angle:libGLESv2",
-    ]
-  }
+  data_deps = [
+    "//third_party/angle:libEGL",
+    "//third_party/angle:libGLESv2",
+  ]
 }
 
 source_set("flatland_unittests") {
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_v3.cc b/ui/ozone/platform/wayland/host/zwp_text_input_v3.cc
index d67a33b..bc9e5db 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_v3.cc
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_v3.cc
@@ -98,6 +98,17 @@
 
 }  // namespace
 
+ZwpTextInputV3Impl::ImeData::ImeData() = default;
+ZwpTextInputV3Impl::ImeData::~ImeData() = default;
+void ZwpTextInputV3Impl::ImeData::Reset() {
+  surrounding_text.reset();
+  cursor_rect.reset();
+  content_type.reset();
+}
+
+ZwpTextInputV3Impl::InputEvents::InputEvents() = default;
+ZwpTextInputV3Impl::InputEvents::~InputEvents() = default;
+
 ZwpTextInputV3Impl::ZwpTextInputV3Impl(
     WaylandConnection* connection,
     zwp_text_input_manager_v3* text_input_manager)
@@ -122,8 +133,8 @@
 ZwpTextInputV3Impl::~ZwpTextInputV3Impl() = default;
 
 void ZwpTextInputV3Impl::Reset() {
-  // Clear last sent values.
-  ResetLastSentValues();
+  // Clear last committed values.
+  ResetCommittedImeData();
   // There is no explicit reset API in v3. See [1].
   // So use disable+enable to force a reset.
   //
@@ -146,8 +157,8 @@
   // internally. So we shouldn't keep old surrounding text anyway. See related
   // crbug.com/353915732 where surrounding text update is not sent after reset
   // when composition is canceled.
-  ResetPendingSetRequests();
-  ResetPendingInputEvents();
+  ResetPendingImeData();
+  ResetInputEventsState();
   zwp_text_input_v3_enable(obj_.get());
   Commit();
 }
@@ -165,40 +176,46 @@
 
 void ZwpTextInputV3Impl::Enable() {
   // Pending state is reset on enable.
-  ResetPendingSetRequests();
-  ResetPendingInputEvents();
+  ResetPendingImeData();
+  ResetInputEventsState();
   zwp_text_input_v3_enable(obj_.get());
   Commit();
 }
 
 void ZwpTextInputV3Impl::Disable() {
   // Avoid sending pending requests if done is received after disabling.
-  ResetPendingSetRequests();
+  ResetPendingImeData();
   // Do not process pending input events after deactivating.
-  ResetPendingInputEvents();
+  ResetInputEventsState();
   zwp_text_input_v3_disable(obj_.get());
   Commit();
 }
 
+bool ZwpTextInputV3Impl::DoneSerialEqualsCommitCount() {
+  return committed_ime_data_.commit_count ==
+         pending_input_events_.last_done_serial;
+}
+
 void ZwpTextInputV3Impl::SetCursorRect(const gfx::Rect& rect) {
-  if (last_sent_cursor_rect_ == rect) {
+  if (committed_ime_data_.cursor_rect &&
+      *committed_ime_data_.cursor_rect == rect) {
     // This is to avoid a loop in sending cursor rect and receiving pre-edit
     // string.
     return;
   }
-  if (commit_count_ != last_done_serial_) {
-    pending_set_cursor_rect_ = rect;
-    return;
-  }
-  SendCursorRect(rect);
-  Commit();
+  pending_ime_data_.cursor_rect = std::make_unique<gfx::Rect>(rect);
+  SendPendingImeData();
 }
 
-void ZwpTextInputV3Impl::SendCursorRect(const gfx::Rect& rect) {
-  CHECK_EQ(commit_count_, last_done_serial_);
-  zwp_text_input_v3_set_cursor_rectangle(obj_.get(), rect.x(), rect.y(),
-                                         rect.width(), rect.height());
-  last_sent_cursor_rect_ = rect;
+bool ZwpTextInputV3Impl::SendCursorRect() {
+  CHECK(DoneSerialEqualsCommitCount());
+  if (const auto& rect = pending_ime_data_.cursor_rect; rect) {
+    zwp_text_input_v3_set_cursor_rectangle(obj_.get(), rect->x(), rect->y(),
+                                           rect->width(), rect->height());
+    committed_ime_data_.cursor_rect = std::move(pending_ime_data_.cursor_rect);
+    return true;
+  }
+  return false;
 }
 
 void ZwpTextInputV3Impl::SetSurroundingText(
@@ -237,24 +254,26 @@
     cursor = base::checked_cast<int32_t>(
         selection_range.IsValid() ? selection_range.end() : text.length());
   }
-  SetSurroundingTextData data{std::move(text), cursor, anchor};
-  if (last_sent_surrounding_text_data_ == data) {
+  auto data =
+      std::make_unique<SetSurroundingTextData>(std::move(text), cursor, anchor);
+  if (committed_ime_data_.surrounding_text &&
+      *committed_ime_data_.surrounding_text == *data) {
     return;
   }
-  if (commit_count_ != last_done_serial_) {
-    pending_set_surrounding_text_ = std::move(data);
-    return;
-  }
-  SendSurroundingText(data);
-  Commit();
+  pending_ime_data_.surrounding_text = std::move(data);
+  SendPendingImeData();
 }
 
-void ZwpTextInputV3Impl::SendSurroundingText(
-    const SetSurroundingTextData& data) {
-  CHECK_EQ(commit_count_, last_done_serial_);
-  zwp_text_input_v3_set_surrounding_text(obj_.get(), data.text.c_str(),
-                                         data.cursor, data.anchor);
-  last_sent_surrounding_text_data_ = data;
+bool ZwpTextInputV3Impl::SendSurroundingText() {
+  CHECK(DoneSerialEqualsCommitCount());
+  if (const auto& data = pending_ime_data_.surrounding_text) {
+    zwp_text_input_v3_set_surrounding_text(obj_.get(), data->text.c_str(),
+                                           data->cursor, data->anchor);
+    committed_ime_data_.surrounding_text =
+        std::move(pending_ime_data_.surrounding_text);
+    return true;
+  }
+  return false;
 }
 
 void ZwpTextInputV3Impl::SetContentType(ui::TextInputType type,
@@ -268,67 +287,65 @@
   if (!should_do_learning) {
     content_hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA;
   }
-  ContentType content_type{content_hint, content_purpose};
-  if (last_sent_content_type_ == content_type) {
+  auto content_type =
+      std::make_unique<ContentType>(content_hint, content_purpose);
+  if (committed_ime_data_.content_type &&
+      *committed_ime_data_.content_type == *content_type) {
     return;
   }
-  if (commit_count_ != last_done_serial_) {
-    pending_set_content_type_ = content_type;
+  pending_ime_data_.content_type = std::move(content_type);
+  SendPendingImeData();
+}
+
+bool ZwpTextInputV3Impl::SendContentType() {
+  CHECK(DoneSerialEqualsCommitCount());
+  if (const auto& content_type = pending_ime_data_.content_type) {
+    zwp_text_input_v3_set_content_type(obj_.get(), content_type->content_hint,
+                                       content_type->content_purpose);
+    committed_ime_data_.content_type =
+        std::move(pending_ime_data_.content_type);
+    return true;
+  }
+  return false;
+}
+
+void ZwpTextInputV3Impl::SendPendingImeData() {
+  if (!DoneSerialEqualsCommitCount()) {
     return;
   }
-  SendContentType(content_type);
-  Commit();
-}
-
-void ZwpTextInputV3Impl::SendContentType(const ContentType& content_type) {
-  CHECK_EQ(commit_count_, last_done_serial_);
-  zwp_text_input_v3_set_content_type(obj_.get(), content_type.content_hint,
-                                     content_type.content_purpose);
-  last_sent_content_type_ = content_type;
-}
-
-void ZwpTextInputV3Impl::ApplyPendingSetRequests() {
   bool commit = false;
-  if (auto content_type = pending_set_content_type_) {
-    SendContentType(*content_type);
+  if (SendContentType()) {
     commit = true;
   }
-  if (auto cursor_rect = pending_set_cursor_rect_) {
-    SendCursorRect(*cursor_rect);
-  }
-  if (auto surrounding_text = pending_set_surrounding_text_) {
-    SendSurroundingText(*surrounding_text);
+  if (SendCursorRect()) {
     commit = true;
   }
-  // clear pending set requests
-  ResetPendingSetRequests();
+  if (SendSurroundingText()) {
+    commit = true;
+  }
   if (commit) {
     Commit();
   }
 }
 
-void ZwpTextInputV3Impl::ResetPendingSetRequests() {
-  pending_set_cursor_rect_.reset();
-  pending_set_content_type_.reset();
-  pending_set_surrounding_text_.reset();
+void ZwpTextInputV3Impl::ResetPendingImeData() {
+  pending_ime_data_.Reset();
 }
 
-void ZwpTextInputV3Impl::ResetLastSentValues() {
-  last_sent_cursor_rect_.reset();
-  last_sent_content_type_.reset();
-  last_sent_surrounding_text_data_.reset();
+void ZwpTextInputV3Impl::ResetCommittedImeData() {
+  committed_ime_data_.Reset();
 }
 
-void ZwpTextInputV3Impl::ResetPendingInputEvents() {
-  pending_preedit_.reset();
-  pending_commit_.reset();
+void ZwpTextInputV3Impl::ResetInputEventsState() {
+  pending_input_events_.preedit = std::nullopt;
+  pending_input_events_.commit = std::nullopt;
 }
 
 void ZwpTextInputV3Impl::Commit() {
   zwp_text_input_v3_commit(obj_.get());
   // It will wrap around to 0 once it reaches uint32_t max value. It is
   // expected that this will occur on the compositor side as well.
-  ++commit_count_;
+  ++committed_ime_data_.commit_count;
 }
 
 void ZwpTextInputV3Impl::OnEnter(void* data,
@@ -353,15 +370,15 @@
                                          int32_t cursor_begin,
                                          int32_t cursor_end) {
   auto* self = static_cast<ZwpTextInputV3Impl*>(data);
-  self->pending_preedit_ = {text ? text : std::string(), cursor_begin,
-                            cursor_end};
+  self->pending_input_events_.preedit = {text ? text : std::string(),
+                                         cursor_begin, cursor_end};
 }
 
 void ZwpTextInputV3Impl::OnCommitString(void* data,
                                         struct zwp_text_input_v3* text_input,
                                         const char* text) {
   auto* self = static_cast<ZwpTextInputV3Impl*>(data);
-  self->pending_commit_ = text ? text : std::string();
+  self->pending_input_events_.commit = text ? text : std::string();
 }
 
 void ZwpTextInputV3Impl::OnDeleteSurroundingText(
@@ -377,17 +394,18 @@
                                 uint32_t serial) {
   // TODO(crbug.com/40113488) apply delete surrounding
   auto* self = static_cast<ZwpTextInputV3Impl*>(data);
+  self->pending_input_events_.last_done_serial = serial;
 
   if (!self->client_) {
-    self->last_done_serial_ = serial;
     return;
   }
 
-  if (const auto& commit_string = self->pending_commit_) {
+  if (const auto& commit_string = self->pending_input_events_.commit) {
     // Replace the existing preedit with the commit string.
     self->client_->OnCommitString(commit_string->c_str());
   }
-  if (const auto& preedit_data = self->pending_preedit_) {
+  if (const auto preedit_data = self->pending_input_events_.preedit) {
+    // Finally process any new preedit string.
     gfx::Range preedit_cursor =
         (preedit_data->cursor_begin < 0 || preedit_data->cursor_end < 0)
             ? gfx::Range::InvalidRange()
@@ -396,12 +414,8 @@
                                    preedit_cursor);
   }
 
-  // reset the input event state.
-  self->ResetPendingInputEvents();
-  self->last_done_serial_ = serial;
-  if (self->last_done_serial_ == self->commit_count_) {
-    self->ApplyPendingSetRequests();
-  }
+  self->ResetInputEventsState();
+  self->SendPendingImeData();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_v3.h b/ui/ozone/platform/wayland/host/zwp_text_input_v3.h
index 9a456eb..c244bde 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_v3.h
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_v3.h
@@ -8,6 +8,7 @@
 #include <text-input-unstable-v3-client-protocol.h>
 
 #include <cstdint>
+#include <optional>
 #include <string>
 
 #include "base/memory/raw_ptr.h"
@@ -115,13 +116,39 @@
     int32_t cursor_end = 0;
   };
 
-  void SendCursorRect(const gfx::Rect& rect);
-  void SendContentType(const ContentType& content_type);
-  void SendSurroundingText(const SetSurroundingTextData& data);
-  void ApplyPendingSetRequests();
-  void ResetPendingSetRequests();
-  void ResetLastSentValues();
-  void ResetPendingInputEvents();
+  // Text input state received from IME, to be applied on done event.
+  struct InputEvents {
+    InputEvents();
+    ~InputEvents();
+    std::optional<PreeditData> preedit;
+    std::optional<std::string> commit;
+    uint32_t last_done_serial = 0;
+  };
+
+  // Data sent to IME.
+  struct ImeData {
+    ImeData();
+    ~ImeData();
+    void Reset();
+    std::unique_ptr<gfx::Rect> cursor_rect;
+    std::unique_ptr<ContentType> content_type;
+    std::unique_ptr<SetSurroundingTextData> surrounding_text;
+    // Only used when committed.
+    uint32_t commit_count = 0;
+  };
+
+  bool DoneSerialEqualsCommitCount();
+
+  bool SendCursorRect();
+  bool SendContentType();
+  bool SendSurroundingText();
+
+  void SendPendingImeData();
+
+  void ResetPendingImeData();
+  void ResetCommittedImeData();
+  void ResetInputEventsState();
+
   void Commit();
 
   // zwp_text_input_v3_listener
@@ -150,22 +177,15 @@
   const raw_ptr<WaylandConnection> connection_;
   wl::Object<zwp_text_input_v3> obj_;
   raw_ptr<ZwpTextInputV3Client> client_;
-  uint32_t commit_count_ = 0;
-  uint32_t last_done_serial_ = 0;
 
-  // Pending input events that will be applied in done event.
-  std::optional<PreeditData> pending_preedit_;
-  std::optional<std::string> pending_commit_;
+  // Input events state that will be applied in done event.
+  InputEvents pending_input_events_;
 
-  // Pending set requests to be sent to compositor
-  std::optional<gfx::Rect> pending_set_cursor_rect_;
-  std::optional<ContentType> pending_set_content_type_;
-  std::optional<SetSurroundingTextData> pending_set_surrounding_text_;
+  // Pending data to be sent to IME.
+  ImeData pending_ime_data_;
 
-  // last sent values
-  std::optional<gfx::Rect> last_sent_cursor_rect_;
-  std::optional<ContentType> last_sent_content_type_;
-  std::optional<SetSurroundingTextData> last_sent_surrounding_text_data_;
+  // Data that was last sent to IME and committed.
+  ImeData committed_ime_data_;
 };
 
 }  // namespace ui
diff --git a/v8 b/v8
index d7fa052..770751e 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit d7fa05296b658db5ecdc143f6cc60185c60001d0
+Subproject commit 770751e8c5989e59409a67716cceb8085f3d23ed