diff --git a/DEPS b/DEPS
index dd981f1..533d932 100644
--- a/DEPS
+++ b/DEPS
@@ -299,7 +299,7 @@
   # 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': '07afa62babe959ab4e6759263490db189e2195c2',
+  'skia_revision': 'c408daec0f9a46d5ed2f698a172ede3fd9f78551',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -307,7 +307,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'f6b40bc8397af944cb09ecb606b2d809466a5f02',
+  'angle_revision': 'db9624073324a2e5b485df7ba24c4cd45d9dceab',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # 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': 'e9092b12dc3cf617d47578f13a1f64285cfa5b2f',
+  'googletest_revision': '7e2c425db2c2e024b2807bfe6d386f4ff068d0d6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -371,11 +371,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'ea769d2a2a7bedc43ac1baec43fdf7423047184e',
+  'catapult_revision': 'aa341ec41f6d475102eee85ddec60d403ef575cd',
   # 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': '675d2d63880904c537f03a76f021c2c13e20e355',
+  'crossbench_revision': '0d4bbb09c3901288772039f71f071897432d77b8',
   # 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': '9520a835b578aa11db9df1cf6651dbe5c3376887',
+  'devtools_frontend_revision': 'd8f563509959c9132c81f22536bff9edd4325093',
   # 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,11 +415,11 @@
   # 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': 'ba5fbf6ca75113fc554651f2cd601ce911f3e6a1',
+  'dawn_revision': '4d33621217632e25d8e88e2f0dd2544596d129ac',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '35b5350ab0afe83a60dbf23ac0f4585e901db7f8',
+  'quiche_revision': 'f0a8dcea08196573119875267b4f8303241a24d9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ink
   # and whatever else without interference from each other.
@@ -527,7 +527,7 @@
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       '0e242589e53523da3fc2df7ee965f9534550dec5',
+  'libcxx_revision':       'cdae0b78c315e58661273c8cd9119b460e68f98b',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:e5c4d1881b85b82789b7013233a944cf1a46370f',
@@ -1521,7 +1521,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '9d5f4e3ec164804c21a8a7c92eed98c1e0094c13',
+    '4d4ffc26471ca1d5eb0d122765d87b359956f635',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -2013,7 +2013,7 @@
 
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '749b3f1960ffd7575f6e7d5ecf1e90d6b9db673a',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd255a8d41e7a2fdc6b50fee69e70014f875d47ef',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2934,7 +2934,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '2ce86c46cefa1c8e62951f54c812b6faf69860ef',
+    Var('webrtc_git') + '/src.git' + '@' + 'e37b3246716888f66e859f17931f2cc7caedbbfd',
 
   # 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.
@@ -2980,7 +2980,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/linux-amd64',
-          'version': '292Pn0vLgzx9408oFYJWgwBQOo-DbERMVgSoEJ6UfwAC',
+          'version': 'lkxqvhJtv-45S0zRCu7Z3er-IcDc6t931-7jM1lN-RMC',
         },
       ],
       'dep_type': 'cipd',
@@ -2990,7 +2990,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'gPfWQkZSNWaJpihXXBc7SCvVDlFrPBXvtdq2BSa6OIoC',
+          'version': '1pUE-hNBek5om-0mUbI4sJSEt4-m1AwEGnVNuEfMFOIC',
         },
       ],
       'dep_type': 'cipd',
@@ -3001,7 +3001,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'v4HGLZ-ir5Y-4pZMGKYMAgJ7yAEv5Y5Kj-cdpYfzlgoC',
+          'version': 'r2LyNXR63hRBteBs8GN14Otjpbnu_VQvVOvDVUFjeV4C',
         },
       ],
       'dep_type': 'cipd',
@@ -3012,7 +3012,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-arm64',
-          'version': 'i6y9edid-Os3HU7qmVuxE-DH00jNj4Pbl6td-HnHYnYC',
+          'version': 'tJUKq9z8lfdk4KydEare4F1gdGAXnTVuC5W3k8mBQBMC',
         },
       ],
       'dep_type': 'cipd',
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 938ed884..0a0498c 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -67,7 +67,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/crash/content/browser/crash_handler_host_linux.h"
 #include "components/embedder_support/origin_trials/origin_trials_settings_storage.h"
diff --git a/android_webview/browser/aw_contents_io_thread_client.cc b/android_webview/browser/aw_contents_io_thread_client.cc
index af0f37d..bb73748 100644
--- a/android_webview/browser/aw_contents_io_thread_client.cc
+++ b/android_webview/browser/aw_contents_io_thread_client.cc
@@ -30,7 +30,7 @@
 #include "base/synchronization/lock.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "components/embedder_support/android/util/features.h"
 #include "components/embedder_support/android/util/input_stream.h"
 #include "components/embedder_support/android/util/web_resource_response.h"
diff --git a/android_webview/browser/aw_cookie_access_policy.cc b/android_webview/browser/aw_cookie_access_policy.cc
index d3d8ed3..7f5f939 100644
--- a/android_webview/browser/aw_cookie_access_policy.cc
+++ b/android_webview/browser/aw_cookie_access_policy.cc
@@ -9,7 +9,7 @@
 #include "android_webview/browser/aw_contents_io_thread_client.h"
 #include "base/check_op.h"
 #include "base/no_destructor.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/render_frame_host.h"
diff --git a/android_webview/browser/aw_field_trials.cc b/android_webview/browser/aw_field_trials.cc
index 4b8ce8ff..69be6d3 100644
--- a/android_webview/browser/aw_field_trials.cc
+++ b/android_webview/browser/aw_field_trials.cc
@@ -263,4 +263,9 @@
   // Disable draw cutout edge-to-edge on WebView. Safe area insets are not
   // handled correctly when WebView is drawing edge-to-edge.
   aw_feature_overrides.DisableFeature(features::kDrawCutoutEdgeToEdge);
+
+  // This is enabled for WebView to improve crbug.com/418159642.
+  // TODO(crbug.com/422161917): Revert this for the ablation study.
+  aw_feature_overrides.EnableFeature(
+      features::kServiceWorkerBackgroundUpdateForRegisteredStorageKeys);
 }
diff --git a/android_webview/browser/gfx/display_webview.cc b/android_webview/browser/gfx/display_webview.cc
index d51c67e..2567936 100644
--- a/android_webview/browser/gfx/display_webview.cc
+++ b/android_webview/browser/gfx/display_webview.cc
@@ -7,6 +7,7 @@
 #include "android_webview/browser/gfx/overlay_processor_webview.h"
 #include "android_webview/browser/gfx/root_frame_sink.h"
 #include "base/memory/ptr_util.h"
+#include "base/trace_event/trace_id_helper.h"
 #include "components/viz/common/features.h"
 #include "components/viz/service/display/overlay_processor_stub.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
diff --git a/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc b/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
index 5b9cfe5..72291e0 100644
--- a/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
+++ b/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
@@ -40,7 +40,7 @@
 #include "base/notreached.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "components/embedder_support/android/util/input_stream.h"
 #include "components/embedder_support/android/util/response_delegate_impl.h"
 #include "components/embedder_support/android/util/web_resource_response.h"
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index fc8242e..1474993 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -1073,7 +1073,7 @@
                 "PrefetchServiceWorker",
                 "Enables SpeculationRules prefetch to ServiceWorker-controlled URLs."),
         Flag.baseFeature("TimedHTMLParserBudget"),
-        Flag.baseFeature("BackgroundUpdateForRegisteredStorageKeys"),
+        Flag.baseFeature("ServiceWorkerBackgroundUpdateForRegisteredStorageKeys"),
         // Add new commandline switches and features above. The final entry should have a
         // trailing comma for cleaner diffs.
     };
diff --git a/ash/app_list/quick_app_access_model.h b/ash/app_list/quick_app_access_model.h
index d293e4c..657011d 100644
--- a/ash/app_list/quick_app_access_model.h
+++ b/ash/app_list/quick_app_access_model.h
@@ -12,10 +12,7 @@
 #include "ash/public/cpp/app_list/app_list_controller_observer.h"
 #include "base/observer_list.h"
 #include "base/scoped_observation.h"
-
-namespace base {
-class TimeTicks;
-}  // namespace base
+#include "base/time/time.h"
 
 namespace gfx {
 class ImageSkia;
@@ -43,11 +40,10 @@
     // Called when the default icon for the quick app icon changes.
     virtual void OnQuickAppIconChanged() = 0;
   };
-  QuickAppAccessModel();
 
+  QuickAppAccessModel();
   QuickAppAccessModel(const QuickAppAccessModel&) = delete;
   QuickAppAccessModel& operator=(const QuickAppAccessModel&) = delete;
-
   ~QuickAppAccessModel() override;
 
   void AddObserver(Observer* observer);
@@ -67,8 +63,10 @@
   // Returns the quick app's display name.
   const std::u16string GetAppName() const;
 
-  const std::string& quick_app_id() { return quick_app_id_; }
-  bool quick_app_should_show_state() { return quick_app_should_show_state_; }
+  const std::string& quick_app_id() const { return quick_app_id_; }
+  bool quick_app_should_show_state() const {
+    return quick_app_should_show_state_;
+  }
 
  private:
   // AppListItemObserver:
diff --git a/ash/public/cpp/holding_space/holding_space_progress.cc b/ash/public/cpp/holding_space/holding_space_progress.cc
index 94febf0..607e2962 100644
--- a/ash/public/cpp/holding_space/holding_space_progress.cc
+++ b/ash/public/cpp/holding_space/holding_space_progress.cc
@@ -5,6 +5,7 @@
 #include "ash/public/cpp/holding_space/holding_space_progress.h"
 
 #include <limits>
+#include <tuple>
 
 #include "base/check_op.h"
 
diff --git a/ash/public/cpp/holding_space/holding_space_progress.h b/ash/public/cpp/holding_space/holding_space_progress.h
index d903481..b5bd973 100644
--- a/ash/public/cpp/holding_space/holding_space_progress.h
+++ b/ash/public/cpp/holding_space/holding_space_progress.h
@@ -5,6 +5,8 @@
 #ifndef ASH_PUBLIC_CPP_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_H_
 #define ASH_PUBLIC_CPP_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_H_
 
+#include <stdint.h>
+
 #include <optional>
 
 #include "ash/public/cpp/ash_public_export.h"
diff --git a/ash/public/cpp/system_tray.h b/ash/public/cpp/system_tray.h
index 1304cbe..ac000dda 100644
--- a/ash/public/cpp/system_tray.h
+++ b/ash/public/cpp/system_tray.h
@@ -6,9 +6,9 @@
 #define ASH_PUBLIC_CPP_SYSTEM_TRAY_H_
 
 #include <string>
+#include <vector>
 
 #include "ash/ash_export.h"
-
 #include "base/memory/raw_ptr.h"
 
 namespace ash {
diff --git a/ash/public/cpp/tab_cluster/correlation_clusterer.h b/ash/public/cpp/tab_cluster/correlation_clusterer.h
index 50f3787b..33fe4ab 100644
--- a/ash/public/cpp/tab_cluster/correlation_clusterer.h
+++ b/ash/public/cpp/tab_cluster/correlation_clusterer.h
@@ -5,6 +5,8 @@
 #ifndef ASH_PUBLIC_CPP_TAB_CLUSTER_CORRELATION_CLUSTERER_H_
 #define ASH_PUBLIC_CPP_TAB_CLUSTER_CORRELATION_CLUSTERER_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <optional>
 #include <set>
diff --git a/ash/public/cpp/wallpaper/sea_pen_image.h b/ash/public/cpp/wallpaper/sea_pen_image.h
index 55d9d2a..085a3ef 100644
--- a/ash/public/cpp/wallpaper/sea_pen_image.h
+++ b/ash/public/cpp/wallpaper/sea_pen_image.h
@@ -5,6 +5,8 @@
 #ifndef ASH_PUBLIC_CPP_WALLPAPER_SEA_PEN_IMAGE_H_
 #define ASH_PUBLIC_CPP_WALLPAPER_SEA_PEN_IMAGE_H_
 
+#include <stdint.h>
+
 #include <string>
 
 #include "ash/public/cpp/ash_public_export.h"
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_decoder.h b/ash/quick_pair/common/fast_pair/fast_pair_decoder.h
index 412ba979..8fd91cf 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_decoder.h
+++ b/ash/quick_pair/common/fast_pair/fast_pair_decoder.h
@@ -7,6 +7,7 @@
 
 #include <cstdint>
 #include <optional>
+#include <string>
 #include <vector>
 
 #include "base/component_export.h"
diff --git a/ash/user_education/user_education_delegate.h b/ash/user_education/user_education_delegate.h
index 9ba2f05..3f48016e 100644
--- a/ash/user_education/user_education_delegate.h
+++ b/ash/user_education/user_education_delegate.h
@@ -5,6 +5,8 @@
 #ifndef ASH_USER_EDUCATION_USER_EDUCATION_DELEGATE_H_
 #define ASH_USER_EDUCATION_USER_EDUCATION_DELEGATE_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <optional>
 #include <string>
diff --git a/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts b/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts
index 2c8121e..a6b0ca66 100644
--- a/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts
+++ b/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts
@@ -120,12 +120,18 @@
     const size = await this.getInputTokenSize(text, session);
 
     if (size < MIN_TOKEN_LENGTH) {
+      console.warn(
+        `Skip GenAI model execution: too small token size: ${size}.`,
+      );
       return {
         kind: 'error',
         error: ModelExecutionError.UNSUPPORTED_TRANSCRIPTION_IS_TOO_SHORT,
       };
     }
     if (size > this.modelInfo.inputTokenLimit) {
+      console.warn(
+        `Skip GenAI model execution: too large token size: ${size}.`,
+      );
       return {
         kind: 'error',
         error: ModelExecutionError.UNSUPPORTED_TRANSCRIPTION_IS_TOO_LONG,
@@ -168,6 +174,7 @@
     // When the model returns the canned response, show the same UI as
     // unsafe content for now.
     if (isCannedResponse(result)) {
+      console.warn('Invalid GenAI result: canned response.');
       return {kind: 'error', error: ModelExecutionError.UNSAFE};
     }
 
@@ -177,6 +184,7 @@
           parsedResult,
           expectedBulletPointCount,
         )) {
+      console.warn('Invalid GenAI result: invalid format.');
       return {kind: 'error', error: ModelExecutionError.UNSAFE};
     }
 
@@ -189,6 +197,7 @@
 
     // Show unsafe content if no valid bullet point.
     if (finalBulletPoints.length === 0) {
+      console.warn('Invalid GenAI result: no valid bullet point.');
       return {kind: 'error', error: ModelExecutionError.UNSAFE};
     }
 
@@ -264,6 +273,7 @@
       return {kind: 'error', error: ModelExecutionError.GENERAL};
     }
     if (await this.contentIsUnsafe(prompt, requestSafetyFeature, language)) {
+      console.warn('Unsafe GenAI prompt.');
       return {kind: 'error', error: ModelExecutionError.UNSAFE};
     }
     const response = await this.executeRaw(
@@ -280,6 +290,7 @@
           responseSafetyFeature,
           language,
         )) {
+      console.warn('Unsafe GenAI result.');
       return {kind: 'error', error: ModelExecutionError.UNSAFE};
     }
     return {kind: 'success', result: response.result};
@@ -508,6 +519,9 @@
   override async loadAndExecute(content: string, language: LanguageCode):
     Promise<ModelResponse<T>> {
     if (!this.platformHandler.getLangPackInfo(language).isGenAiSupported) {
+      console.warn(
+        `Skip GenAI model execution: unsupported language: ${language}.`,
+      );
       return {kind: 'error', error: ModelExecutionError.UNSUPPORTED_LANGUAGE};
     }
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 9ea4ead..3e8c3be 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -885,7 +885,6 @@
     "timer/wall_clock_timer.h",
     "token.cc",
     "token.h",
-    "trace_event/base_tracing.h",
     "trace_event/base_tracing_forward.h",
     "trace_event/common/trace_event_common.h",
     "trace_event/heap_profiler_allocation_context.cc",
diff --git a/base/allocator/partition_alloc_features.cc b/base/allocator/partition_alloc_features.cc
index b2942fc..352530e3 100644
--- a/base/allocator/partition_alloc_features.cc
+++ b/base/allocator/partition_alloc_features.cc
@@ -480,10 +480,6 @@
              FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
-BASE_FEATURE(kPartitionAllocUseSmallSingleSlotSpans,
-             "PartitionAllocUseSmallSingleSlotSpans",
-             FEATURE_ENABLED_BY_DEFAULT);
-
 #if PA_CONFIG(ENABLE_SHADOW_METADATA)
 BASE_FEATURE(kPartitionAllocShadowMetadata,
              "PartitionAllocShadowMetadata",
diff --git a/base/allocator/partition_alloc_support.cc b/base/allocator/partition_alloc_support.cc
index 59ed4e2..abb7274 100644
--- a/base/allocator/partition_alloc_support.cc
+++ b/base/allocator/partition_alloc_support.cc
@@ -41,7 +41,7 @@
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "partition_alloc/allocation_guard.h"
 #include "partition_alloc/buildflags.h"
@@ -1183,10 +1183,6 @@
 #endif  // PA_BUILDFLAG(ENABLE_PARTITION_LOCK_PRIORITY_INHERITANCE) &&
         // PA_BUILDFLAG(IS_ANDROID)
 
-  allocator_shim::UseSmallSingleSlotSpans use_small_single_slot_spans(
-      base::FeatureList::IsEnabled(
-          features::kPartitionAllocUseSmallSingleSlotSpans));
-
   allocator_shim::ConfigurePartitions(
       allocator_shim::EnableBrp(brp_config.enable_brp),
       brp_config.extra_extras_size,
@@ -1195,8 +1191,7 @@
       scheduler_loop_quarantine_global_config,
       scheduler_loop_quarantine_thread_local_config,
       allocator_shim::EventuallyZeroFreedMemory(eventually_zero_freed_memory),
-      allocator_shim::FewerMemoryRegions(fewer_memory_regions),
-      use_small_single_slot_spans);
+      allocator_shim::FewerMemoryRegions(fewer_memory_regions));
 
   const uint32_t extras_size = allocator_shim::GetMainPartitionRootExtrasSize();
   // As per description, extras are optional and are expected not to
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 cc0d1ef..56b8c54 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
@@ -6022,7 +6022,7 @@
 TEST_P(PartitionAllocTest, SortActiveSlotSpans) {
   auto run_test = [this](size_t count) {
     PartitionBucket bucket;
-    bucket.Init(16, /*use_small_single_slot_spans=*/false);
+    bucket.Init(16);
     bucket.active_slot_spans_head = nullptr;
 
 #if PA_CONFIG(ENABLE_SHADOW_METADATA)
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 80e056f..80a353d 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.cc
@@ -605,8 +605,7 @@
   return ComputeSystemPagesPerSlotSpanInternal(slot_size);
 }
 
-void PartitionBucket::Init(uint32_t new_slot_size,
-                           bool use_small_single_slot_spans) {
+void PartitionBucket::Init(uint32_t new_slot_size) {
   slot_size = new_slot_size;
   slot_size_reciprocal = kReciprocalMask / new_slot_size + 1;
   active_slot_spans_head = SlotSpanMetadata<
@@ -625,7 +624,7 @@
       ComputeSystemPagesPerSlotSpan(slot_size, prefer_smaller_slot_spans);
   PA_CHECK(num_system_pages_per_slot_span > 0);
 
-  InitCanStoreRawSize(use_small_single_slot_spans);
+  InitCanStoreRawSize();
 }
 
 PA_ALWAYS_INLINE SlotSpanMetadata<MetadataKind::kReadOnly>*
@@ -710,7 +709,7 @@
   return slot_span;
 }
 
-void PartitionBucket::InitCanStoreRawSize(bool use_small_single_slot_spans) {
+void PartitionBucket::InitCanStoreRawSize() {
   // By definition, direct map buckets can store the raw size. The value
   // of `can_store_raw_size` is set explicitly in that code path (see
   // `PartitionDirectMap()`), bypassing this method.
@@ -726,8 +725,7 @@
   if (slot_size <= MaxRegularSlotSpanSize()) [[likely]] {
     // Even when the slot size is below the standard floor for single
     // slot spans, there exist spans that happen to have exactly one
-    // slot per. If `use_small_single_slot_spans` is true, we use more
-    // nuanced criteria for determining if a span is "single-slot."
+    // slot per.
     //
     // The conditions are all of:
     // *  Don't deal with slots trafficked by the thread cache [1].
@@ -744,9 +742,9 @@
     // [2] ../../PartitionAlloc.md#layout-in-memory
     const bool not_handled_by_thread_cache =
         slot_size > kThreadCacheLargeSizeThreshold;
-    can_store_raw_size =
-        use_small_single_slot_spans && not_handled_by_thread_cache &&
-        get_slots_per_span() == 1u && get_pages_per_slot_span() > 1u;
+    can_store_raw_size = not_handled_by_thread_cache &&
+                         get_slots_per_span() == 1u &&
+                         get_pages_per_slot_span() > 1u;
     return;
   }
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.h b/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.h
index 6dfc34bf..6c3841dc 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_bucket.h
@@ -66,7 +66,7 @@
 
   // Public API.
   PA_COMPONENT_EXPORT(PARTITION_ALLOC)
-  void Init(uint32_t new_slot_size, bool use_small_single_slot_spans);
+  void Init(uint32_t new_slot_size);
 
   // Sets |is_already_zeroed| to true if the allocation was satisfied by
   // requesting (a) new page(s) from the operating system, or false otherwise.
@@ -176,7 +176,7 @@
 
  private:
   // Sets `this->can_store_raw_size`.
-  void InitCanStoreRawSize(bool use_small_single_slot_spans);
+  void InitCanStoreRawSize();
 
   // Allocates several consecutive super pages. Returns the address of the first
   // super page.
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 7715637..4a97575f 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_root.cc
@@ -1191,14 +1191,11 @@
     // This is a "magic" value so we can test if a root pointer is valid.
     inverted_self = ~reinterpret_cast<uintptr_t>(this);
 
-    const bool use_small_single_slot_spans =
-        opts.use_small_single_slot_spans == PartitionOptions::kEnabled;
-
     // Set up the actual usable buckets first.
     for (size_t bucket_index = 0; bucket_index < BucketIndexLookup::kNumBuckets;
          ++bucket_index) {
       const size_t slot_size = BucketIndexLookup::GetBucketSize(bucket_index);
-      buckets[bucket_index].Init(slot_size, use_small_single_slot_spans);
+      buckets[bucket_index].Init(slot_size);
     }
 
 #if !PA_CONFIG(THREAD_CACHE_SUPPORTED)
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 997940d..899099c 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_root.h
@@ -198,8 +198,6 @@
 #if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
   ThreadIsolationOption thread_isolation;
 #endif
-
-  EnableToggle use_small_single_slot_spans = kDisabled;
 };
 
 constexpr PartitionOptions::PartitionOptions() = default;
diff --git a/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_unittest.cc b/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_unittest.cc
index 8fdda5e2..f02743f 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_unittest.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_unittest.cc
@@ -1620,7 +1620,7 @@
     !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
 
 void HandleOOM(size_t unused_size) {
-  LOG(FATAL) << "Out of memory";
+  PA_LOG(FATAL) << "Out of memory";
 }
 
 class BackupRefPtrTest : public testing::Test {
diff --git a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim.h b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim.h
index dd18e49..d43a9fff 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim.h
@@ -149,9 +149,6 @@
 using FewerMemoryRegions =
     partition_alloc::internal::base::StrongAlias<class FewerMemoryRegionsTag,
                                                  bool>;
-using UseSmallSingleSlotSpans = partition_alloc::internal::base::
-    StrongAlias<class UseSmallSingleSlotSpansTag, bool>;
-
 // If |thread_cache_on_non_quarantinable_partition| is specified, the
 // thread-cache will be enabled on the non-quarantinable partition. The
 // thread-cache on the main (malloc) partition will be disabled.
@@ -167,8 +164,7 @@
     partition_alloc::internal::SchedulerLoopQuarantineConfig
         scheduler_loop_quarantine_thread_local_config,
     EventuallyZeroFreedMemory eventually_zero_freed_memory,
-    FewerMemoryRegions fewer_memory_regions,
-    UseSmallSingleSlotSpans use_small_single_slot_spans);
+    FewerMemoryRegions fewer_memory_regions);
 
 PA_COMPONENT_EXPORT(ALLOCATOR_SHIM) uint32_t GetMainPartitionRootExtrasSize();
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.cc b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.cc
index bf13d4a2..b7e026b 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.cc
@@ -639,8 +639,7 @@
     partition_alloc::internal::SchedulerLoopQuarantineConfig
         scheduler_loop_quarantine_thread_local_config,
     EventuallyZeroFreedMemory eventually_zero_freed_memory,
-    FewerMemoryRegions fewer_memory_regions,
-    UseSmallSingleSlotSpans use_small_single_slot_spans) {
+    FewerMemoryRegions fewer_memory_regions) {
   // Calling Get() is actually important, even if the return value isn't
   // used, because it has a side effect of initializing the variable, if it
   // wasn't already.
@@ -680,10 +679,6 @@
                            ? partition_alloc::PartitionOptions::kEnabled
                            : partition_alloc::PartitionOptions::kDisabled,
             .reporting_mode = memory_tagging_reporting_mode};
-        opts.use_small_single_slot_spans =
-            use_small_single_slot_spans
-                ? partition_alloc::PartitionOptions::kEnabled
-                : partition_alloc::PartitionOptions::kDisabled;
         return opts;
       }());
   partition_alloc::PartitionRoot* new_root = new_main_allocator->root();
diff --git a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h
index 29206a5..d969bc9 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h
@@ -189,14 +189,12 @@
 
   auto eventually_zero_freed_memory = EventuallyZeroFreedMemory(false);
   auto fewer_memory_regions = FewerMemoryRegions(false);
-  auto use_small_single_slot_spans = UseSmallSingleSlotSpans(true);
 
   ConfigurePartitions(enable_brp, brp_extra_extras_size, enable_memory_tagging,
                       memory_tagging_reporting_mode, distribution,
                       scheduler_loop_quarantine_global_config,
                       scheduler_loop_quarantine_thread_local_config,
-                      eventually_zero_freed_memory, fewer_memory_regions,
-                      use_small_single_slot_spans);
+                      eventually_zero_freed_memory, fewer_memory_regions);
 }
 #endif  // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
diff --git a/base/android/application_status_listener.cc b/base/android/application_status_listener.cc
index 45b191d..6efdf26 100644
--- a/base/android/application_status_listener.cc
+++ b/base/android/application_status_listener.cc
@@ -11,7 +11,7 @@
 #include "base/metrics/user_metrics.h"
 #include "base/observer_list_threadsafe.h"
 #include "base/trace_event/application_state_proto_android.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
 #include "base/tasks_jni/ApplicationStatus_jni.h"
diff --git a/base/android/early_trace_event_binding.cc b/base/android/early_trace_event_binding.cc
index 4b28b29..c6b471a 100644
--- a/base/android/early_trace_event_binding.cc
+++ b/base/android/early_trace_event_binding.cc
@@ -9,7 +9,7 @@
 #include "base/android/jni_string.h"
 #include "base/android/trace_event_binding.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/tracing_buildflags.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
diff --git a/base/android/jank_metric_uma_recorder.cc b/base/android/jank_metric_uma_recorder.cc
index a7bb1f9..ebb362b 100644
--- a/base/android/jank_metric_uma_recorder.cc
+++ b/base/android/jank_metric_uma_recorder.cc
@@ -11,7 +11,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "jank_metric_uma_recorder.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
diff --git a/base/android/jni_registrar.cc b/base/android/jni_registrar.cc
index 9172a93..827a33f 100644
--- a/base/android/jni_registrar.cc
+++ b/base/android/jni_registrar.cc
@@ -11,7 +11,7 @@
 
 #include "base/android/jni_android.h"
 #include "base/logging.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 namespace android {
diff --git a/base/android/meminfo_dump_provider.cc b/base/android/meminfo_dump_provider.cc
index 20637d3..4f77e01 100644
--- a/base/android/meminfo_dump_provider.cc
+++ b/base/android/meminfo_dump_provider.cc
@@ -8,9 +8,10 @@
 
 #include "base/android/jni_android.h"
 #include "base/logging.h"
-#include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/memory_jni/MemoryInfoBridge_jni.h"
+#include "base/time/time.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base::android {
 
diff --git a/base/android/meminfo_dump_provider.h b/base/android/meminfo_dump_provider.h
index 582ac37..8549281 100644
--- a/base/android/meminfo_dump_provider.h
+++ b/base/android/meminfo_dump_provider.h
@@ -8,7 +8,8 @@
 #include "base/base_export.h"
 #include "base/no_destructor.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base::android {
 
diff --git a/base/android/meminfo_dump_provider_unittest.cc b/base/android/meminfo_dump_provider_unittest.cc
index f587355..7cab734 100644
--- a/base/android/meminfo_dump_provider_unittest.cc
+++ b/base/android/meminfo_dump_provider_unittest.cc
@@ -9,7 +9,9 @@
 #include <string>
 
 #include "base/android/build_info.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base::android {
diff --git a/base/android/pre_freeze_background_memory_trimmer.cc b/base/android/pre_freeze_background_memory_trimmer.cc
index a7b8c6f..0c36746 100644
--- a/base/android/pre_freeze_background_memory_trimmer.cc
+++ b/base/android/pre_freeze_background_memory_trimmer.cc
@@ -27,8 +27,8 @@
 #include "base/task/thread_pool.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/named_trigger.h"  // no-presubmit-check
+#include "base/trace_event/trace_event.h"
 
 namespace base::android {
 BASE_FEATURE(kShouldFreezeSelf, "ShouldFreezeSelf", FEATURE_ENABLED_BY_DEFAULT);
diff --git a/base/android/scoped_input_event.h b/base/android/scoped_input_event.h
index 9ad8be70..12709ba5 100644
--- a/base/android/scoped_input_event.h
+++ b/base/android/scoped_input_event.h
@@ -9,7 +9,7 @@
 
 #include "base/base_export.h"
 #include "base/memory/raw_ptr.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/typed_macros.h"
 
 namespace base::android {
 
diff --git a/base/android/sys_utils.cc b/base/android/sys_utils.cc
index 7e2a006..3d003bc 100644
--- a/base/android/sys_utils.cc
+++ b/base/android/sys_utils.cc
@@ -10,7 +10,7 @@
 #include "base/feature_list.h"
 #include "base/process/process_metrics.h"
 #include "base/system/sys_info.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
 #include "base/sys_utils_jni/SysUtils_jni.h"
diff --git a/base/android/task_scheduler/task_runner_android.cc b/base/android/task_scheduler/task_runner_android.cc
index 0347d550..a38220f 100644
--- a/base/android/task_scheduler/task_runner_android.cc
+++ b/base/android/task_scheduler/task_runner_android.cc
@@ -20,7 +20,7 @@
 #include "base/task/thread_pool/thread_pool_impl.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
 #include "base/android_runtime_jni_headers/Runnable_jni.h"
diff --git a/base/android/trace_event_binding.cc b/base/android/trace_event_binding.cc
index 9c1377b5..cf659b08 100644
--- a/base/android/trace_event_binding.cc
+++ b/base/android/trace_event_binding.cc
@@ -11,10 +11,10 @@
 #include "base/android/jni_string.h"
 #include "base/compiler_specific.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/trace_event/base_tracing.h"
-#include "base/tracing_buildflags.h"
-
 #include "base/trace_event/trace_event_impl.h"  // no-presubmit-check
+#include "base/trace_event/trace_id_helper.h"
+#include "base/trace_event/typed_macros.h"
+#include "base/tracing_buildflags.h"
 #include "third_party/perfetto/include/perfetto/tracing/track.h"  // no-presubmit-check nogncheck
 #include "third_party/perfetto/protos/perfetto/config/chrome/chrome_config.gen.h"  // nogncheck
 
diff --git a/base/debug/dump_without_crashing.cc b/base/debug/dump_without_crashing.cc
index 7862bc1..22dc925b 100644
--- a/base/debug/dump_without_crashing.cc
+++ b/base/debug/dump_without_crashing.cc
@@ -12,7 +12,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "base/synchronization/lock.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/buildflag.h"
 
 namespace {
diff --git a/base/files/file.cc b/base/files/file.cc
index e62d6d40..d9e2b9f 100644
--- a/base/files/file.cc
+++ b/base/files/file.cc
@@ -13,7 +13,7 @@
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/timer/elapsed_timer.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
diff --git a/base/files/file_path.cc b/base/files/file_path.cc
index 878a15e..8a97119 100644
--- a/base/files/file_path.cc
+++ b/base/files/file_path.cc
@@ -25,7 +25,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_ostream_operators.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 #if BUILDFLAG(IS_APPLE)
 #include "base/apple/scoped_cftyperef.h"
diff --git a/base/files/file_path_watcher_inotify.cc b/base/files/file_path_watcher_inotify.cc
index 43b97b0..85d70a0 100644
--- a/base/files/file_path_watcher_inotify.cc
+++ b/base/files/file_path_watcher_inotify.cc
@@ -43,7 +43,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/files/file_tracing.cc b/base/files/file_tracing.cc
index 69953e5..c0d6315a 100644
--- a/base/files/file_tracing.cc
+++ b/base/files/file_tracing.cc
@@ -7,7 +7,7 @@
 #include <atomic>
 
 #include "base/files/file.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 
diff --git a/base/location.cc b/base/location.cc
index 931758a0..f4ce271 100644
--- a/base/location.cc
+++ b/base/location.cc
@@ -7,7 +7,7 @@
 #include "base/compiler_specific.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 #if defined(COMPILER_MSVC)
 #include <intrin.h>
diff --git a/base/location_unittest.cc b/base/location_unittest.cc
index 2e3c25e5..bb06f734 100644
--- a/base/location_unittest.cc
+++ b/base/location_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "base/location.h"
 
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/perfetto/include/perfetto/test/traced_value_test_support.h"
diff --git a/base/logging.cc b/base/logging.cc
index b8eee13..3a470897 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -47,11 +47,13 @@
 #include "base/task/common/task_annotator.h"
 #include "base/test/scoped_logging_settings.h"
 #include "base/threading/platform_thread.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/interned_args_helper.h"
+#include "base/trace_event/typed_macros.h"
 #include "base/vlog.h"
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/base/internal/raw_logging.h"
 #include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h"
 
 #if !BUILDFLAG(IS_NACL)
 #include "base/auto_reset.h"
diff --git a/base/memory/madv_free_discardable_memory_allocator_posix.h b/base/memory/madv_free_discardable_memory_allocator_posix.h
index 3e12720f..e0a1fc6 100644
--- a/base/memory/madv_free_discardable_memory_allocator_posix.h
+++ b/base/memory/madv_free_discardable_memory_allocator_posix.h
@@ -16,7 +16,8 @@
 #include "base/memory/discardable_memory.h"
 #include "base/memory/discardable_memory_allocator.h"
 #include "base/memory/madv_free_discardable_memory_posix.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/memory/memory_pressure_listener.cc b/base/memory/memory_pressure_listener.cc
index d536066..7846d75 100644
--- a/base/memory/memory_pressure_listener.cc
+++ b/base/memory/memory_pressure_listener.cc
@@ -9,8 +9,10 @@
 #include "base/observer_list.h"
 #include "base/observer_list_threadsafe.h"
 #include "base/task/sequenced_task_runner.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/interned_args_helper.h"
+#include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/memory_pressure_level_proto.h"
+#include "base/trace_event/trace_event.h"
 #include "base/tracing_buildflags.h"
 
 namespace base {
diff --git a/base/memory/shared_memory_tracker.cc b/base/memory/shared_memory_tracker.cc
index c7609cc..d9d883c 100644
--- a/base/memory/shared_memory_tracker.cc
+++ b/base/memory/shared_memory_tracker.cc
@@ -8,9 +8,9 @@
 
 #include "base/check.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event.h"
 #include "base/tracing_buildflags.h"
 
 namespace base {
diff --git a/base/memory/shared_memory_tracker.h b/base/memory/shared_memory_tracker.h
index 1a3bb251..45d88e3 100644
--- a/base/memory/shared_memory_tracker.h
+++ b/base/memory/shared_memory_tracker.h
@@ -11,7 +11,7 @@
 #include "base/base_export.h"
 #include "base/memory/shared_memory_mapping.h"
 #include "base/synchronization/lock.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/memory_dump_provider.h"
 
 namespace base {
 
diff --git a/base/message_loop/message_pump_default.cc b/base/message_loop/message_pump_default.cc
index 10358797..f61747c9 100644
--- a/base/message_loop/message_pump_default.cc
+++ b/base/message_loop/message_pump_default.cc
@@ -8,7 +8,7 @@
 #include "base/logging.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_APPLE)
diff --git a/base/message_loop/message_pump_epoll.cc b/base/message_loop/message_pump_epoll.cc
index 008728bb5..48c7f5f 100644
--- a/base/message_loop/message_pump_epoll.cc
+++ b/base/message_loop/message_pump_epoll.cc
@@ -27,7 +27,8 @@
 #include "base/posix/eintr_wrapper.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/heap_profiler.h"
+#include "base/trace_event/trace_event.h"
 
 #if DCHECK_IS_ON()
 #include <iomanip>
diff --git a/base/message_loop/message_pump_fuchsia.cc b/base/message_loop/message_pump_fuchsia.cc
index adc3986b..5e758b2 100644
--- a/base/message_loop/message_pump_fuchsia.cc
+++ b/base/message_loop/message_pump_fuchsia.cc
@@ -15,7 +15,7 @@
 #include "base/check.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/logging.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 
diff --git a/base/message_loop/message_pump_win.cc b/base/message_loop/message_pump_win.cc
index d33f686f..4e20010 100644
--- a/base/message_loop/message_pump_win.cc
+++ b/base/message_loop/message_pump_win.cc
@@ -25,7 +25,8 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/task_features.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/interned_args_helper.h"
+#include "base/trace_event/typed_macros.h"
 #include "base/tracing_buildflags.h"
 #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_message_pump.pbzero.h"
 
diff --git a/base/metrics/user_metrics.cc b/base/metrics/user_metrics.cc
index 07325827..aa9f052 100644
--- a/base/metrics/user_metrics.cc
+++ b/base/metrics/user_metrics.cc
@@ -14,7 +14,7 @@
 #include "base/location.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 namespace {
diff --git a/base/power_monitor/cpu_frequency_utils.cc b/base/power_monitor/cpu_frequency_utils.cc
index 9c16dfa..4885ccb 100644
--- a/base/power_monitor/cpu_frequency_utils.cc
+++ b/base/power_monitor/cpu_frequency_utils.cc
@@ -8,7 +8,7 @@
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_WIN)
diff --git a/base/power_monitor/power_monitor.cc b/base/power_monitor/power_monitor.cc
index df28a48..68a7441 100644
--- a/base/power_monitor/power_monitor.cc
+++ b/base/power_monitor/power_monitor.cc
@@ -9,7 +9,7 @@
 #include "base/logging.h"
 #include "base/no_destructor.h"
 #include "base/power_monitor/power_monitor_source.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "power_observer.h"
 
diff --git a/base/power_monitor/power_monitor.h b/base/power_monitor/power_monitor.h
index 6213ed7..c8f7efd 100644
--- a/base/power_monitor/power_monitor.h
+++ b/base/power_monitor/power_monitor.h
@@ -12,7 +12,7 @@
 #include "base/observer_list_threadsafe.h"
 #include "base/power_monitor/power_observer.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/power_monitor/speed_limit_observer_win.cc b/base/power_monitor/speed_limit_observer_win.cc
index 70782b11..c2e2f790 100644
--- a/base/power_monitor/speed_limit_observer_win.cc
+++ b/base/power_monitor/speed_limit_observer_win.cc
@@ -18,7 +18,7 @@
 #include "base/power_monitor/cpu_frequency_utils.h"
 #include "base/system/sys_info.h"
 #include "base/timer/elapsed_timer.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace {
diff --git a/base/process/current_process.h b/base/process/current_process.h
index be41d24..9773afe 100644
--- a/base/process/current_process.h
+++ b/base/process/current_process.h
@@ -13,8 +13,8 @@
 #include "base/no_destructor.h"
 #include "base/process/process_handle.h"
 #include "base/synchronization/lock.h"
-#include "base/trace_event/base_tracing.h"
 #include "build/buildflag.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"
 
 namespace tracing {
 class TraceEventDataSource;
diff --git a/base/process/launch_fuchsia.cc b/base/process/launch_fuchsia.cc
index ced30570..79ee18b 100644
--- a/base/process/launch_fuchsia.cc
+++ b/base/process/launch_fuchsia.cc
@@ -24,7 +24,7 @@
 #include "base/process/environment_internal.h"
 #include "base/scoped_generic.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 
diff --git a/base/process/launch_mac.cc b/base/process/launch_mac.cc
index 29e6d750..116f4b86 100644
--- a/base/process/launch_mac.cc
+++ b/base/process/launch_mac.cc
@@ -20,7 +20,7 @@
 #include "base/process/environment_internal.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/threading/thread_restrictions.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 #if BUILDFLAG(IS_MAC)
 #include "base/apple/mach_port_rendezvous_mac.h"
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
index 6433a7e..7b1d5f9 100644
--- a/base/process/launch_posix.cc
+++ b/base/process/launch_posix.cc
@@ -41,7 +41,7 @@
 #include "base/threading/platform_thread.h"
 #include "base/threading/platform_thread_internal_posix.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc
index 3399c0e8..1dd6a6da 100644
--- a/base/process/launch_win.cc
+++ b/base/process/launch_win.cc
@@ -37,7 +37,7 @@
 #include "base/threading/scoped_thread_priority.h"
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/scoped_process_information.h"
 #include "base/win/startup_information.h"
diff --git a/base/process/process_fuchsia.cc b/base/process/process_fuchsia.cc
index 14ce8ca..75c509e 100644
--- a/base/process/process_fuchsia.cc
+++ b/base/process/process_fuchsia.cc
@@ -14,7 +14,7 @@
 #include "base/fuchsia/default_job.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/threading/thread_restrictions.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 #if BUILDFLAG(CLANG_PROFILING)
 #include "base/test/clang_profiling.h"
diff --git a/base/process/process_metrics_apple.mm b/base/process/process_metrics_apple.mm
index 6375b63d..a4fe040b 100644
--- a/base/process/process_metrics_apple.mm
+++ b/base/process/process_metrics_apple.mm
@@ -24,7 +24,7 @@
 #include "base/numerics/safe_math.h"
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/expected.h"
 #include "build/build_config.h"
 
diff --git a/base/process/process_metrics_fuchsia.cc b/base/process/process_metrics_fuchsia.cc
index 4b5dfd5..f14d8c5 100644
--- a/base/process/process_metrics_fuchsia.cc
+++ b/base/process/process_metrics_fuchsia.cc
@@ -9,7 +9,7 @@
 
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index f2110ea..d327b12 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -38,7 +38,7 @@
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
 #include "base/threading/thread_restrictions.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/expected.h"
 #include "base/values.h"
 #include "build/build_config.h"
diff --git a/base/process/process_metrics_win.cc b/base/process/process_metrics_win.cc
index 113c58f6..4d2ba62 100644
--- a/base/process/process_metrics_win.cc
+++ b/base/process/process_metrics_win.cc
@@ -19,7 +19,7 @@
 #include "base/notreached.h"
 #include "base/system/sys_info.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "build/build_config.h"
 
diff --git a/base/process/process_posix.cc b/base/process/process_posix.cc
index d19747e..8e8c6acda 100644
--- a/base/process/process_posix.cc
+++ b/base/process/process_posix.cc
@@ -22,7 +22,7 @@
 #include "base/process/kill.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_MAC)
diff --git a/base/process/process_win.cc b/base/process/process_win.cc
index 1dbf497..34020ce3d 100644
--- a/base/process/process_win.cc
+++ b/base/process/process_win.cc
@@ -11,7 +11,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/process/kill.h"
 #include "base/threading/thread_restrictions.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/win/win_util.h"
 #include "base/win/windows_version.h"
 
diff --git a/base/profiler/libunwindstack_unwinder_android.cc b/base/profiler/libunwindstack_unwinder_android.cc
index 531b533..c13916c 100644
--- a/base/profiler/libunwindstack_unwinder_android.cc
+++ b/base/profiler/libunwindstack_unwinder_android.cc
@@ -15,7 +15,7 @@
 #include "base/profiler/module_cache.h"
 #include "base/profiler/native_unwinder_android.h"
 #include "base/profiler/profile_builder.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/typed_macros.h"
 #include "build/build_config.h"
 #include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Elf.h"
 #include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Error.h"
diff --git a/base/profiler/stack_copier_signal.cc b/base/profiler/stack_copier_signal.cc
index 46009f7..ce877e2 100644
--- a/base/profiler/stack_copier_signal.cc
+++ b/base/profiler/stack_copier_signal.cc
@@ -29,7 +29,7 @@
 #include "base/profiler/stack_buffer.h"
 #include "base/profiler/suspendable_thread_delegate.h"
 #include "base/time/time_override.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc
index c44dc12..5450d925 100644
--- a/base/profiler/stack_sampling_profiler.cc
+++ b/base/profiler/stack_sampling_profiler.cc
@@ -29,7 +29,7 @@
 #include "base/threading/thread.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_WIN)
diff --git a/base/run_loop.cc b/base/run_loop.cc
index 35056c0..d135ee7e 100644
--- a/base/run_loop.cc
+++ b/base/run_loop.cc
@@ -11,7 +11,7 @@
 #include "base/functional/callback.h"
 #include "base/observer_list.h"
 #include "base/task/single_thread_task_runner.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/synchronization/cancelable_event.cc b/base/synchronization/cancelable_event.cc
index f34bf20..d3c3d2b1f 100644
--- a/base/synchronization/cancelable_event.cc
+++ b/base/synchronization/cancelable_event.cc
@@ -6,7 +6,7 @@
 #include "base/notreached.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/synchronization/waitable_event.cc b/base/synchronization/waitable_event.cc
index 79581c14..de226a0 100644
--- a/base/synchronization/waitable_event.cc
+++ b/base/synchronization/waitable_event.cc
@@ -11,7 +11,7 @@
 
 #include "base/check.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/tracing_buildflags.h"
 
 namespace base {
diff --git a/base/task/common/task_annotator.cc b/base/task/common/task_annotator.cc
index 5654417b..135db033 100644
--- a/base/task/common/task_annotator.cc
+++ b/base/task/common/task_annotator.cc
@@ -19,9 +19,13 @@
 #include "base/logging.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/heap_profiler.h"
+#include "base/trace_event/interned_args_helper.h"
+#include "base/trace_event/trace_event.h"
+#include "base/tracing/protos/chrome_track_event.pbzero.h"
 #include "base/tracing_buildflags.h"
 #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_mojo_event_info.pbzero.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/task_execution.pbzero.h"
 
 namespace base {
 
diff --git a/base/task/common/task_annotator.h b/base/task/common/task_annotator.h
index e7ed932f..7922462b 100644
--- a/base/task/common/task_annotator.h
+++ b/base/task/common/task_annotator.h
@@ -14,7 +14,7 @@
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/pending_task.h"
 #include "base/time/tick_clock.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/pass_key.h"
 
 namespace base {
diff --git a/base/task/current_thread.cc b/base/task/current_thread.cc
index 53a6213a..0f5ac15e4 100644
--- a/base/task/current_thread.cc
+++ b/base/task/current_thread.cc
@@ -14,7 +14,7 @@
 #include "base/message_loop/message_pump_type.h"
 #include "base/task/sequence_manager/sequence_manager_impl.h"
 #include "base/threading/thread_local.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc
index 7b1f2b0c..04539c9e 100644
--- a/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -43,7 +43,7 @@
 #include "base/threading/thread_id_name_manager.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/blink_buildflags.h"
 #include "build/build_config.h"
 
diff --git a/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index fd30aa7..f025b981c 100644
--- a/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -67,7 +67,7 @@
 #include "base/threading/sequence_local_storage_slot.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/tracing_buildflags.h"
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/base/task/sequence_manager/task_queue.cc b/base/task/sequence_manager/task_queue.cc
index cd41040..c9bcab39 100644
--- a/base/task/sequence_manager/task_queue.cc
+++ b/base/task/sequence_manager/task_queue.cc
@@ -19,7 +19,7 @@
 #include "base/threading/thread_checker.h"
 #include "base/threading/thread_checker_impl.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base::sequence_manager {
 
diff --git a/base/task/sequence_manager/task_queue.h b/base/task/sequence_manager/task_queue.h
index b87c189..8a5389a 100644
--- a/base/task/sequence_manager/task_queue.h
+++ b/base/task/sequence_manager/task_queue.h
@@ -20,8 +20,8 @@
 #include "base/task/task_observer.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/base_tracing_forward.h"
+#include "base/tracing/protos/chrome_track_event.pbzero.h"
 
 namespace perfetto {
 class EventContext;
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index 21a00bf..933fc9e 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -36,7 +36,8 @@
 #include "base/task/task_observer.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/interned_args_helper.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/container/inlined_vector.h"
 
diff --git a/base/task/sequence_manager/task_queue_selector.cc b/base/task/sequence_manager/task_queue_selector.cc
index cc552427..4d6c8610 100644
--- a/base/task/sequence_manager/task_queue_selector.cc
+++ b/base/task/sequence_manager/task_queue_selector.cc
@@ -14,7 +14,7 @@
 #include "base/task/sequence_manager/work_queue.h"
 #include "base/task/task_features.h"
 #include "base/threading/thread_checker.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base::sequence_manager::internal {
 
diff --git a/base/task/sequence_manager/thread_controller.cc b/base/task/sequence_manager/thread_controller.cc
index e22c5df..87a9eb1 100644
--- a/base/task/sequence_manager/thread_controller.cc
+++ b/base/task/sequence_manager/thread_controller.cc
@@ -18,7 +18,7 @@
 #include "base/strings/string_util.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base::sequence_manager::internal {
 
diff --git a/base/task/sequence_manager/thread_controller.h b/base/task/sequence_manager/thread_controller.h
index ffa65fa..2ad391a5 100644
--- a/base/task/sequence_manager/thread_controller.h
+++ b/base/task/sequence_manager/thread_controller.h
@@ -28,7 +28,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/thread_annotations.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/tracing_buildflags.h"
 #include "build/build_config.h"
 
diff --git a/base/task/sequence_manager/thread_controller_impl.cc b/base/task/sequence_manager/thread_controller_impl.cc
index 0c2f3a9..8ca3ec03 100644
--- a/base/task/sequence_manager/thread_controller_impl.cc
+++ b/base/task/sequence_manager/thread_controller_impl.cc
@@ -14,7 +14,7 @@
 #include "base/task/common/lazy_now.h"
 #include "base/task/sequence_manager/sequence_manager_impl.h"
 #include "base/task/sequence_manager/sequenced_task_source.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace base::sequence_manager::internal {
diff --git a/base/task/sequence_manager/thread_controller_power_monitor.cc b/base/task/sequence_manager/thread_controller_power_monitor.cc
index ad7abc2..fd250b27 100644
--- a/base/task/sequence_manager/thread_controller_power_monitor.cc
+++ b/base/task/sequence_manager/thread_controller_power_monitor.cc
@@ -6,7 +6,7 @@
 
 #include "base/feature_list.h"
 #include "base/power_monitor/power_monitor.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base::sequence_manager::internal {
 
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
index 8fa6327e..f6f5ca48 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
@@ -22,7 +22,7 @@
 #include "base/threading/hang_watcher.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_IOS)
diff --git a/base/task/thread_pool/job_task_source.cc b/base/task/thread_pool/job_task_source.cc
index e7ef71b..417d6e6 100644
--- a/base/task/thread_pool/job_task_source.cc
+++ b/base/task/thread_pool/job_task_source.cc
@@ -19,7 +19,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "base/time/time_override.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base::internal {
 
diff --git a/base/task/thread_pool/task_tracker.cc b/base/task/thread_pool/task_tracker.cc
index df19d8c..96231a9 100644
--- a/base/task/thread_pool/task_tracker.cc
+++ b/base/task/thread_pool/task_tracker.cc
@@ -31,7 +31,8 @@
 #include "base/threading/sequence_local_storage_map.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
+#include "base/tracing/protos/chrome_track_event.pbzero.h"
 #include "base/values.h"
 #include "build/build_config.h"
 
diff --git a/base/task/thread_pool/thread_group_impl.cc b/base/task/thread_pool/thread_group_impl.cc
index cad8c8e..bc8fa19 100644
--- a/base/task/thread_pool/thread_group_impl.cc
+++ b/base/task/thread_pool/thread_group_impl.cc
@@ -17,7 +17,7 @@
 #include "base/threading/scoped_blocking_call_internal.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time_override.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "third_party/abseil-cpp/absl/container/inlined_vector.h"
 
 namespace base::internal {
diff --git a/base/task/thread_pool/worker_thread.cc b/base/task/thread_pool/worker_thread.cc
index cdd5e2c..fb60dd3 100644
--- a/base/task/thread_pool/worker_thread.cc
+++ b/base/task/thread_pool/worker_thread.cc
@@ -21,7 +21,7 @@
 #include "base/threading/hang_watcher.h"
 #include "base/time/time.h"
 #include "base/time/time_override.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "partition_alloc/buildflags.h"
 
diff --git a/base/test/launcher/test_launcher_unittest.cc b/base/test/launcher/test_launcher_unittest.cc
index d594ca7..21b45c2 100644
--- a/base/test/launcher/test_launcher_unittest.cc
+++ b/base/test/launcher/test_launcher_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/strings/to_string.h"
 #include "base/test/gtest_xml_util.h"
 #include "base/test/launcher/test_launcher_test_utils.h"
 #include "base/test/launcher/unit_test_launcher.h"
diff --git a/base/test/test_pending_task.cc b/base/test/test_pending_task.cc
index 54a56b6..ffff9d4 100644
--- a/base/test/test_pending_task.cc
+++ b/base/test/test_pending_task.cc
@@ -7,7 +7,8 @@
 #include <string>
 #include <utility>
 
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/traced_value.h"
 
 namespace base {
 
diff --git a/base/test/test_pending_task_unittest.cc b/base/test/test_pending_task_unittest.cc
index 0345d9e..58050e4 100644
--- a/base/test/test_pending_task_unittest.cc
+++ b/base/test/test_pending_task_unittest.cc
@@ -5,7 +5,8 @@
 #include "base/test/test_pending_task.h"
 
 #include "base/functional/bind.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/traced_value.h"
 #include "base/tracing_buildflags.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest-spi.h"
diff --git a/base/test/test_trace_processor_example_unittest.cc b/base/test/test_trace_processor_example_unittest.cc
index a61db72..a728ed65 100644
--- a/base/test/test_trace_processor_example_unittest.cc
+++ b/base/test/test_trace_processor_example_unittest.cc
@@ -5,6 +5,7 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_trace_processor.h"
 #include "base/test/trace_test_utils.h"
+#include "base/trace_event/trace_event.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/perfetto/include/perfetto/tracing/tracing.h"
diff --git a/base/test/trace_event_analyzer.h b/base/test/trace_event_analyzer.h
index dad01f12..a2c53f1 100644
--- a/base/test/trace_event_analyzer.h
+++ b/base/test/trace_event_analyzer.h
@@ -100,7 +100,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 class Value;
diff --git a/base/threading/hang_watcher.cc b/base/threading/hang_watcher.cc
index 3f52378..c62f44f 100644
--- a/base/threading/hang_watcher.cc
+++ b/base/threading/hang_watcher.cc
@@ -28,7 +28,7 @@
 #include "base/threading/threading_features.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/threading/platform_thread.cc b/base/threading/platform_thread.cc
index abe6453..2e4f7d9 100644
--- a/base/threading/platform_thread.cc
+++ b/base/threading/platform_thread.cc
@@ -6,7 +6,7 @@
 
 #include "base/task/current_thread.h"
 #include "base/threading/thread_id_name_manager.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 #if BUILDFLAG(IS_FUCHSIA)
 #include "base/fuchsia/scheduler.h"
diff --git a/base/threading/platform_thread_metrics_apple.mm b/base/threading/platform_thread_metrics_apple.mm
index 62db5248..b9c9213 100644
--- a/base/threading/platform_thread_metrics_apple.mm
+++ b/base/threading/platform_thread_metrics_apple.mm
@@ -13,7 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 
diff --git a/base/threading/platform_thread_metrics_fuchsia.cc b/base/threading/platform_thread_metrics_fuchsia.cc
index b1df0b7..429d4e6 100644
--- a/base/threading/platform_thread_metrics_fuchsia.cc
+++ b/base/threading/platform_thread_metrics_fuchsia.cc
@@ -15,7 +15,7 @@
 #include <optional>
 
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 
diff --git a/base/threading/platform_thread_metrics_linux.cc b/base/threading/platform_thread_metrics_linux.cc
index c49e2428..13f0eb30 100644
--- a/base/threading/platform_thread_metrics_linux.cc
+++ b/base/threading/platform_thread_metrics_linux.cc
@@ -16,7 +16,7 @@
 #include "base/process/internal_linux.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 
diff --git a/base/threading/platform_thread_metrics_win.cc b/base/threading/platform_thread_metrics_win.cc
index 73ab3797..e3ba14c 100644
--- a/base/threading/platform_thread_metrics_win.cc
+++ b/base/threading/platform_thread_metrics_win.cc
@@ -13,7 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/win/scoped_handle.h"
 #include "build/build_config.h"
 
diff --git a/base/threading/scoped_blocking_call.cc b/base/threading/scoped_blocking_call.cc
index eb253a2..b9901ca3 100644
--- a/base/threading/scoped_blocking_call.cc
+++ b/base/threading/scoped_blocking_call.cc
@@ -8,7 +8,8 @@
 #include "base/threading/thread_local.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/interned_args_helper.h"
+#include "base/trace_event/typed_macros.h"
 #include "base/tracing_buildflags.h"
 #include "build/build_config.h"
 #include "third_party/perfetto/protos/perfetto/trace/track_event/source_location.pbzero.h"
diff --git a/base/threading/scoped_thread_priority.cc b/base/threading/scoped_thread_priority.cc
index 995f94d..caec5b8 100644
--- a/base/threading/scoped_thread_priority.cc
+++ b/base/threading/scoped_thread_priority.cc
@@ -7,7 +7,8 @@
 #include "base/check_op.h"
 #include "base/location.h"
 #include "base/threading/platform_thread.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/interned_args_helper.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/threading/thread_restrictions.cc b/base/threading/thread_restrictions.cc
index 74e56c3..c5f793b6 100644
--- a/base/threading/thread_restrictions.cc
+++ b/base/threading/thread_restrictions.cc
@@ -6,7 +6,8 @@
 
 #include "base/check.h"
 #include "base/threading/hang_watcher.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/interned_args_helper.h"
+#include "base/trace_event/typed_macros.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/trace_event/base_tracing.h b/base/trace_event/base_tracing.h
deleted file mode 100644
index ea24753..0000000
--- a/base/trace_event/base_tracing.h
+++ /dev/null
@@ -1,30 +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 BASE_TRACE_EVENT_BASE_TRACING_H_
-#define BASE_TRACE_EVENT_BASE_TRACING_H_
-
-// Proxy header that provides tracing instrumentation for //base code. When
-// tracing support is disabled via the gn flag enable_base_tracing, this header
-// provides a mock implementation of the relevant trace macros instead, which
-// causes the instrumentation in //base to be compiled into no-ops.
-
-#include "base/tracing_buildflags.h"
-
-// TODO(crbug.com/42050015): Switch to perfetto for trace event implementation.
-#include "base/trace_event/heap_profiler.h"  // IWYU pragma: export, nogncheck
-#include "base/trace_event/interned_args_helper.h"  // IWYU pragma: export, nogncheck
-#include "base/trace_event/memory_allocator_dump_guid.h"  // IWYU pragma: export, nogncheck
-#include "base/trace_event/memory_dump_manager.h"  // IWYU pragma: export, nogncheck
-#include "base/trace_event/memory_dump_provider.h"  // IWYU pragma: export, nogncheck
-#include "base/trace_event/trace_event.h"      // IWYU pragma: export, nogncheck
-#include "base/trace_event/trace_id_helper.h"  // IWYU pragma: export, nogncheck
-#include "base/trace_event/traced_value.h"     // IWYU pragma: export, nogncheck
-#include "base/trace_event/typed_macros.h"     // IWYU pragma: export, nogncheck
-#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"  // IWYU pragma: export, nogncheck
-#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"  // IWYU pragma: export, nogncheck
-#include "third_party/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h"  // IWYU pragma: export, nogncheck
-#include "third_party/perfetto/protos/perfetto/trace/track_event/task_execution.pbzero.h"  // IWYU pragma: export, nogncheck
-
-#endif  // BASE_TRACE_EVENT_BASE_TRACING_H_
diff --git a/base/trace_event/named_trigger.h b/base/trace_event/named_trigger.h
index 7cbc9c7..17eebf5 100644
--- a/base/trace_event/named_trigger.h
+++ b/base/trace_event/named_trigger.h
@@ -11,7 +11,6 @@
 #include <string>
 
 #include "base/base_export.h"
-#include "base/trace_event/base_tracing.h"
 
 namespace base::trace_event {
 
diff --git a/base/values.cc b/base/values.cc
index 2400671..571d7be 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -27,11 +27,11 @@
 #include "base/notreached.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/memory_usage_estimator.h"  // no-presubmit-check
+#include "base/trace_event/trace_event.h"
 #include "base/tracing_buildflags.h"
 #include "base/types/pass_key.h"
 #include "base/types/to_address.h"
-#include "base/trace_event/memory_usage_estimator.h"  // no-presubmit-check
 
 namespace base {
 
diff --git a/base/win/object_watcher.cc b/base/win/object_watcher.cc
index 642e5510..bbbb6e6 100644
--- a/base/win/object_watcher.cc
+++ b/base/win/object_watcher.cc
@@ -13,7 +13,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/threading/thread_restrictions.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace base {
 namespace win {
diff --git a/base/win/scoped_handle_verifier.cc b/base/win/scoped_handle_verifier.cc
index 8eb96802..aa75354 100644
--- a/base/win/scoped_handle_verifier.cc
+++ b/base/win/scoped_handle_verifier.cc
@@ -18,7 +18,7 @@
 #include "base/memory/raw_ref.h"
 #include "base/notreached.h"
 #include "base/synchronization/lock_impl.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/win/base_win_buildflags.h"
 #include "base/win/current_module.h"
 #include "base/win/scoped_handle.h"
diff --git a/build/android/java/templates/BuildConfig.template b/build/android/java/templates/BuildConfig.template
index c1465a6..3d06cb4 100644
--- a/build/android/java/templates/BuildConfig.template
+++ b/build/android/java/templates/BuildConfig.template
@@ -110,6 +110,12 @@
     public static boolean IS_DESKTOP_ANDROID;
 #endif
 
+#if defined(_ENABLE_DESKTOP_ANDROID_EXTENSIONS)
+    public static boolean ENABLE_DESKTOP_ANDROID_EXTENSIONS = true;
+#else
+    public static boolean ENABLE_DESKTOP_ANDROID_EXTENSIONS;
+#endif
+
     // Controls whether or not StrictModeContext is a no-op.
 #if defined(_DISABLE_STRICT_MODE_CONTEXT)
     public static boolean DISABLE_STRICT_MODE_CONTEXT = true;
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 05f947f4..159a1665 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -9,6 +9,7 @@
 import("//build/config/rts.gni")
 import("//build/config/sanitizers/sanitizers.gni")
 import("//build_overrides/build.gni")
+import("//extensions/buildflags/buildflags.gni")
 
 assert(
     is_android || is_robolectric,
@@ -1998,6 +1999,10 @@
         defines += [ "_IS_DESKTOP_ANDROID" ]
       }
 
+      if (enable_desktop_android_extensions) {
+        defines += [ "_ENABLE_DESKTOP_ANDROID_EXTENSIONS" ]
+      }
+
       if (defined(invoker.write_clang_profiling_data) &&
           invoker.write_clang_profiling_data) {
         defines += [ "_WRITE_CLANG_PROFILING_DATA" ]
diff --git a/build/config/mac/mac_sdk_overrides.gni b/build/config/mac/mac_sdk_overrides.gni
index 15ddfd5c..8f8ac1c 100644
--- a/build/config/mac/mac_sdk_overrides.gni
+++ b/build/config/mac/mac_sdk_overrides.gni
@@ -7,5 +7,5 @@
 
 declare_args() {
   # Minimum supported version of the Mac SDK.
-  mac_sdk_min = "10.15"
+  mac_sdk_min = "15"
 }
diff --git a/build/install-build-deps.py b/build/install-build-deps.py
index f699b9b..6b1236a5 100755
--- a/build/install-build-deps.py
+++ b/build/install-build-deps.py
@@ -376,7 +376,6 @@
       "libsqlite3-0",
       "libuuid1",
       "libwayland-egl1",
-      "libwayland-egl1-mesa",
       "libx11-6",
       "libx11-xcb1",
       "libxau6",
diff --git a/build/toolchain/nacl_toolchain.gni b/build/toolchain/nacl_toolchain.gni
index fbaa65e..915716a 100644
--- a/build/toolchain/nacl_toolchain.gni
+++ b/build/toolchain/nacl_toolchain.gni
@@ -54,6 +54,9 @@
       use_clang_coverage = false
       coverage_instrumentation_input_file = ""
 
+      # NaCl doesn't work with Clang modules.
+      use_libcxx_modules = false
+
       if (use_reclient) {
         if (is_win) {
           reclient_cc_cfg_file = rebase_path(reclient_cfg_dir, root_build_dir) +
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index e4bb2359..4aa235a6 100644
--- a/buildtools/deps_revisions.gni
+++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@
 declare_args() {
   # Used to cause full rebuilds on libc++ rolls. This should be kept in sync
   # with the libcxx_revision var in //DEPS.
-  libcxx_revision = "0e242589e53523da3fc2df7ee965f9534550dec5"
+  libcxx_revision = "cdae0b78c315e58661273c8cd9119b460e68f98b"
 }
diff --git a/buildtools/third_party/libc++/BUILD.gn b/buildtools/third_party/libc++/BUILD.gn
index d88764bb..48a85d7 100644
--- a/buildtools/third_party/libc++/BUILD.gn
+++ b/buildtools/third_party/libc++/BUILD.gn
@@ -61,10 +61,7 @@
   template("sysroot_modules") {
     source_set(target_name) {
       use_libcxx_modules = false
-      deps = []
-      if (is_linux) {
-        sources = [ "//build/linux/amd64/module.modulemap" ]
-      }
+      sources = [ "//build/linux/amd64/module.modulemap" ]
       deps = [
         ":copy_custom_headers",
         ":copy_libcxx_headers",
diff --git a/cc/raster/single_thread_task_graph_runner.cc b/cc/raster/single_thread_task_graph_runner.cc
index 1a2c295..356ec6c 100644
--- a/cc/raster/single_thread_task_graph_runner.cc
+++ b/cc/raster/single_thread_task_graph_runner.cc
@@ -12,8 +12,6 @@
 #include <utility>
 
 #include "base/threading/simple_thread.h"
-#include "base/trace_event/base_tracing.h"
-#include "base/trace_event/trace_event.h"
 #include "base/trace_event/typed_macros.h"
 
 namespace cc {
diff --git a/chrome/VERSION b/chrome/VERSION
index b47a6f8..496154cd 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=139
 MINOR=0
-BUILD=7222
+BUILD=7223
 PATCH=0
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
index ff274a2a..d982ebe 100644
--- a/chrome/android/java/res/values/ids.xml
+++ b/chrome/android/java/res/values/ids.xml
@@ -38,6 +38,7 @@
     <item type="id" name="open_recently_closed_tab" />
     <item type="id" name="toggle_bookmark_bar" />
     <item type="id" name="switch_keyboard_focus_row" />
+    <item type="id" name="open_tab_strip_context_menu" />
     <item type="id" name="focus_bookmarks" />
     <item type="id" name="dev_tools" />
     <item type="id" name="task_manager" />
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java b/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java
index 524484e..6d624f1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java
@@ -673,7 +673,7 @@
         new KeyboardShortcutDefinition(
                 KeyboardShortcutsSemanticMeaning
                         .NOT_IMPLEMENTED_FOCUSED_TAB_STRIP_ITEM_OPEN_CONTEXT_MENU,
-                new KeyCombo(KeyEvent.KEYCODE_F7, KeyEvent.META_SHIFT_ON),
+                new KeyCombo(KeyEvent.KEYCODE_F10, KeyEvent.META_SHIFT_ON),
                 /* resId= */ Resources.ID_NULL,
                 /* groupId= */ Resources.ID_NULL);
         new KeyboardShortcutDefinition(
@@ -1138,6 +1138,14 @@
                     } else {
                         return false;
                     }
+                case KeyboardShortcutsSemanticMeaning
+                        .NOT_IMPLEMENTED_FOCUSED_TAB_STRIP_ITEM_OPEN_CONTEXT_MENU:
+                    if (ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_KEYBOARD_A11Y)) {
+                        return menuOrKeyboardActionController.onMenuOrKeyboardAction(
+                                R.id.open_tab_strip_context_menu, /* fromMenu= */ false);
+                    } else {
+                        return false;
+                    }
                 case KeyboardShortcutsSemanticMeaning.KEYBOARD_FOCUS_TOOLBAR:
                     if (ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_KEYBOARD_A11Y)) {
                         toolbarManager.requestFocus();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
index fdad183..97e4cfe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
@@ -82,6 +82,8 @@
     static final String PREF_CARD_BENEFITS = "card_benefits";
     private static final String PREF_PAYMENT_APPS = "payment_apps";
     static final String PREF_LOYALTY_CARDS = "loyalty_cards";
+    // The constant used to optionally scroll to the loyalty card management pref.
+    static final String EXTRA_FOCUS_LOYALTY_CARD_PREF = "focus_loyalty_card_pref";
 
     @VisibleForTesting
     static final String PREF_FINANCIAL_ACCOUNTS_MANAGEMENT = "financial_accounts_management";
@@ -111,6 +113,11 @@
         screen.setShouldUseGeneratedIds(false);
 
         setPreferenceScreen(screen);
+
+        Bundle arguments = getArguments();
+        if (arguments != null && arguments.keySet().contains(EXTRA_FOCUS_LOYALTY_CARD_PREF)) {
+            scrollToPreference(PREF_LOYALTY_CARDS);
+        }
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/SettingsNavigationHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/SettingsNavigationHelper.java
index a4274b4..ed3c636 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/SettingsNavigationHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/SettingsNavigationHelper.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.autofill.settings;
 
 import android.content.Context;
+import android.os.Bundle;
 
 import org.jni_zero.CalledByNative;
 
@@ -50,6 +51,19 @@
         return true;
     }
 
+    /**
+     * Shows the loyalty card management pref in the payments settings fragment.
+     *
+     * @param context The {@link Context} required to start the settings page.
+     */
+    public static void showGoogleWalletSettings(Context context) {
+        // TODO: crbug.com/421839554 - Record user action for this scenario.
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(AutofillPaymentMethodsFragment.EXTRA_FOCUS_LOYALTY_CARD_PREF, true);
+        SettingsNavigationFactory.createSettingsNavigation()
+                .startSettings(context, AutofillPaymentMethodsFragment.class, bundle);
+    }
+
     @CalledByNative
     private static void showAutofillProfileSettings(WebContents webContents) {
         WindowAndroid windowAndroid = webContents.getTopLevelNativeWindow();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index 3ecb8b5..783cabd6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -2216,6 +2216,21 @@
         mTabContextMenuCoordinator.showMenu(anchorRectProvider, tab.getTabId());
     }
 
+    /**
+     * Opens the context menu for the keyboard-focused view, if applicable.
+     *
+     * @return Whether the context menu was successfully opened.
+     */
+    public boolean openKeyboardFocusedContextMenu() {
+        List<VirtualView> virtualViews = new ArrayList<>();
+        getVirtualViews(virtualViews);
+        for (VirtualView view : virtualViews) {
+            if (!view.isKeyboardFocused()) continue;
+            return showContextMenu((StripLayoutView) view);
+        }
+        return false;
+    }
+
     /* package */ void showTabContextMenuForTesting(StripLayoutTab tab) {
         showTabContextMenu(tab);
     }
@@ -2889,16 +2904,28 @@
         mUpdateHost.requestUpdate();
     }
 
-    private void showContextMenu(StripLayoutView clickedView) {
+    /**
+     * Show the context menu originating at {@param clickedView}, and returns true if a context menu
+     * was shown. (Note: this will return false if there is no context menu to be shown at {@param
+     * clickedView}.
+     *
+     * @param clickedView The view for which to show a context menu.
+     * @return Whether a context menu was shown.
+     */
+    private boolean showContextMenu(StripLayoutView clickedView) {
         if (clickedView instanceof StripLayoutTab clickedTab
                 && ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_STRIP_CONTEXT_MENU)) {
             showTabContextMenu(clickedTab);
+            return true;
         } else if (clickedView instanceof CompositorButton button
                 && button.getType() == ButtonType.TAB_CLOSE) {
             showCloseButtonMenu((StripLayoutTab) button.getParentView());
+            return true;
         } else if (clickedView instanceof StripLayoutGroupTitle groupTitle) {
             showTabGroupContextMenu(groupTitle, /* shouldWaitForUpdate= */ false);
+            return true;
         }
+        return false;
     }
 
     private void handleTabClick(StripLayoutTab tab) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
index b8cc16ce..9e202bd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -1647,4 +1647,13 @@
     public boolean containsKeyboardFocus() {
         return mManagerHost.containsKeyboardFocus(this);
     }
+
+    /**
+     * Opens the context menu for the currently keyboard-focused item, if applicable.
+     *
+     * @return Whether the context menu was successfully opened.
+     */
+    public boolean openKeyboardFocusedContextMenu() {
+        return getActiveStripLayoutHelper().openKeyboardFocusedContextMenu();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java
index 49d068f..1b2b429 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLoadIfNeededCaller;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_public.browser.media.capture.ScreenCapture;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
@@ -195,8 +196,12 @@
     private void startAndroidCapturePrompt() {
         var fragment = MediaCapturePickerHeadlessFragment.getInstanceForCurrentActivity();
         fragment.startAndroidCapturePrompt(
-                result -> {
-                    switch (result) {
+                (action, result) -> {
+                    if (action != CaptureAction.CAPTURE_CANCELLED) {
+                        ScreenCapture.onPick(result);
+                    }
+
+                    switch (action) {
                         case CaptureAction.CAPTURE_CANCELLED:
                             mDelegate.onCancel();
                             break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerHeadlessFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerHeadlessFragment.java
index 8b72a251..999111a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerHeadlessFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerHeadlessFragment.java
@@ -10,6 +10,7 @@
 import android.media.projection.MediaProjectionManager;
 import android.os.Bundle;
 
+import androidx.activity.result.ActivityResult;
 import androidx.activity.result.ActivityResultLauncher;
 import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.IntDef;
@@ -19,7 +20,6 @@
 import androidx.fragment.app.FragmentManager;
 
 import org.chromium.base.ApplicationStatus;
-import org.chromium.base.Callback;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -47,9 +47,13 @@
         int CAPTURE_SCREEN = 2;
     }
 
+    interface Delegate {
+        void onPicked(@CaptureAction int action, ActivityResult result);
+    }
+
     private MediaProjectionManager mMediaProjectionManager;
     private ActivityResultLauncher<Intent> mLauncher;
-    private Callback<@CaptureAction Integer> mNextCallback;
+    private Delegate mNextDelegate;
 
     public static MediaCapturePickerHeadlessFragment getInstanceForCurrentActivity() {
         var activity = (FragmentActivity) ApplicationStatus.getLastTrackedFocusedActivity();
@@ -83,25 +87,25 @@
                 registerForActivityResult(
                         new ActivityResultContracts.StartActivityForResult(),
                         result -> {
-                            assert mNextCallback != null;
+                            assert mNextDelegate != null;
                             if (result.getResultCode() == Activity.RESULT_OK
                                     && result.getData() != null) {
                                 // There is currently no way to differentiate between window and
                                 // screen sharing.
-                                mNextCallback.onResult(CaptureAction.CAPTURE_WINDOW);
+                                mNextDelegate.onPicked(CaptureAction.CAPTURE_WINDOW, result);
                             } else {
-                                mNextCallback.onResult(CaptureAction.CAPTURE_CANCELLED);
+                                mNextDelegate.onPicked(CaptureAction.CAPTURE_CANCELLED, result);
                             }
-                            mNextCallback = null;
+                            mNextDelegate = null;
                         });
 
         // This fragment has no UI so we can retain it.
         setRetainInstance(true);
     }
 
-    public void startAndroidCapturePrompt(Callback<@CaptureAction Integer> callback) {
-        assert mNextCallback == null;
-        mNextCallback = callback;
+    public void startAndroidCapturePrompt(Delegate delegate) {
+        assert mNextDelegate == null;
+        mNextDelegate = delegate;
         mLauncher.launch(mMediaProjectionManager.createScreenCaptureIntent());
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
index 3044b4c..462b1e81 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.multiwindow;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
 import static org.chromium.chrome.browser.tabwindow.TabWindowManager.INVALID_WINDOW_ID;
 
 import android.app.Activity;
@@ -86,6 +87,8 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 class MultiInstanceManagerApi31 extends MultiInstanceManagerImpl implements ActivityStateListener {
     private static final String TAG = "MIMApi31";
@@ -640,6 +643,22 @@
         ChromeSharedPreferences.getInstance().writeInt(taskMapKey(instanceId), taskId);
     }
 
+    static Set<Integer> getPersistedInstanceIds() {
+        Set<Integer> ids = new HashSet<>();
+        Map<String, Long> lastAccessedTimeMap =
+                ChromeSharedPreferences.getInstance()
+                        .readLongsWithPrefix(
+                                ChromePreferenceKeys.MULTI_INSTANCE_LAST_ACCESSED_TIME);
+        for (String prefKey : lastAccessedTimeMap.keySet()) {
+            Pattern pattern = Pattern.compile("(\\d+)$");
+            Matcher matcher = pattern.matcher(prefKey);
+            assert matcher.find() : "Key should be suffixed with the instance id.";
+            assumeNonNull(matcher.group(1));
+            ids.add(Integer.parseInt(matcher.group(1)));
+        }
+        return ids;
+    }
+
     private void removeInvalidInstanceData(boolean cleanupApplicationStatus) {
         // Remove tasks that do not exist any more from the task map
         ActivityManager activityManager =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
index b7e05575..13f9749 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
@@ -63,6 +63,7 @@
 import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 /**
  * Utilities for detecting multi-window/multi-instance support.
@@ -406,8 +407,9 @@
     public static int getInstanceCount() {
         if (sInstanceCountForTesting != null) return sInstanceCountForTesting;
         int count = 0;
-        for (int i = 0; i < getMaxInstances(); ++i) {
-            if (MultiInstanceManagerApi31.instanceEntryExists(i) && isRestorableInstance(i)) {
+        Set<Integer> ids = MultiInstanceManagerApi31.getPersistedInstanceIds();
+        for (Integer id : ids) {
+            if (isRestorableInstance(id)) {
                 count++;
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index dd537d1..9c07946 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -70,6 +70,7 @@
 import org.chromium.chrome.browser.collaboration.messaging.MessagingBackendServiceFactory;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerImpl;
+import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelperManager;
 import org.chromium.chrome.browser.crash.ChromePureJavaExceptionReporter;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.data_sharing.DataSharingNotificationManager;
@@ -1811,6 +1812,12 @@
         if (id == R.id.switch_keyboard_focus_row) {
             mKeyboardFocusRowManager.onKeyboardFocusRowSwitch();
             return true;
+        } else if (id == R.id.open_tab_strip_context_menu) {
+            @Nullable
+            StripLayoutHelperManager stripLayoutHelperManager =
+                    mLayoutManager.getStripLayoutHelperManager();
+            if (stripLayoutHelperManager == null) return false;
+            return stripLayoutHelperManager.openKeyboardFocusedContextMenu();
         } else if (id == R.id.focus_bookmarks) {
             if (mBookmarkBarCoordinator != null) mBookmarkBarCoordinator.requestFocus();
             return true;
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 d3817ad..f17c203 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
@@ -1647,6 +1647,7 @@
                     ExtensionToolbarManager.maybeCreate(
                             mActivity,
                             extensionToolbarStub,
+                            windowAndroid,
                             profileSupplier,
                             tabProvider,
                             browsingModeThemeColorProvider);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerBridge.java
index af2387f..5bdc71aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerBridge.java
@@ -15,6 +15,7 @@
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.browser.autofill.AutofillFallbackSurfaceLauncher;
+import org.chromium.chrome.browser.autofill.settings.SettingsNavigationHelper;
 import org.chromium.ui.base.WindowAndroid;
 
 import java.lang.ref.WeakReference;
@@ -74,6 +75,13 @@
     }
 
     @Override
+    public void showGoogleWalletSettings() {
+        if (mContext.get() != null) {
+            SettingsNavigationHelper.showGoogleWalletSettings(mContext.get());
+        }
+    }
+
+    @Override
     public void creditCardSuggestionSelected(String uniqueId, boolean isVirtual) {
         if (mNativeTouchToFillPaymentMethodViewController != 0) {
             TouchToFillPaymentMethodControllerBridgeJni.get()
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/KeyboardShortcutsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/KeyboardShortcutsTest.java
index a73ddcb..e2dfa339 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/KeyboardShortcutsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/KeyboardShortcutsTest.java
@@ -342,6 +342,16 @@
                         /* id= */ eq(R.id.switch_keyboard_focus_row), /* fromMenu= */ eq(false));
     }
 
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.ANDROID_KEYBOARD_A11Y)
+    public void testOpenStripContextMenu() {
+        keyDown(KeyEvent.KEYCODE_F10, KeyEvent.META_SHIFT_ON, true);
+        verify(mMenuOrKeyboardActionController, times(1))
+                .onMenuOrKeyboardAction(
+                        /* id= */ eq(R.id.open_tab_strip_context_menu), /* fromMenu= */ eq(false));
+    }
+
     private void testOpenBookmarks(
             boolean expectHandled, boolean isCurrentTabVisible, int metaState) {
         assertEquals(expectHandled, keyDown(KeyEvent.KEYCODE_O, metaState, isCurrentTabVisible));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/SettingsNavigationHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/SettingsNavigationHelperTest.java
index 30a6d64..e5d923e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/SettingsNavigationHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/SettingsNavigationHelperTest.java
@@ -5,11 +5,14 @@
 package org.chromium.chrome.browser.autofill.settings;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 
 import android.content.Context;
+import android.os.Bundle;
 
 import androidx.test.filters.SmallTest;
 
@@ -18,6 +21,8 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -39,6 +44,7 @@
 
     @Mock private SettingsNavigation mMockLauncher;
     @Mock private Context mMockContext;
+    @Captor private ArgumentCaptor<Bundle> mBundleCaptor;
 
     private final UserActionTester mActionTester = new UserActionTester();
 
@@ -62,6 +68,23 @@
 
     @Test
     @SmallTest
+    public void testLaunchesGoogleWalletSettings() {
+        SettingsNavigationHelper.showGoogleWalletSettings(mMockContext);
+        verify(mMockLauncher)
+                .startSettings(
+                        eq(mMockContext),
+                        eq(AutofillPaymentMethodsFragment.class),
+                        mBundleCaptor.capture());
+
+        Bundle bundle = mBundleCaptor.getValue();
+        assertNotNull(bundle);
+        assertTrue(
+                bundle.keySet()
+                        .contains(AutofillPaymentMethodsFragment.EXTRA_FOCUS_LOYALTY_CARD_PREF));
+    }
+
+    @Test
+    @SmallTest
     public void testRecordsActionThenLaunchesAddressesSettings() {
         assertTrue(SettingsNavigationHelper.showAutofillProfileSettings(mMockContext));
         assertTrue(mActionTester.getActions().contains("AutofillAddressesViewed"));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
index ab4f825..057abf3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -6295,6 +6295,74 @@
                 /* expectedScrollDelta= */ StripLayoutHelper.SCROLL_SPEED_FACTOR);
     }
 
+    @EnableFeatures({
+        ChromeFeatureList.ANDROID_KEYBOARD_A11Y,
+        ChromeFeatureList.TAB_STRIP_CONTEXT_MENU
+    })
+    @Test
+    public void testOpenContextMenu_notApplicable() {
+        initializeTest(false, false, 0);
+        setupForIndividualTabContextMenu();
+        assertFalse(
+                "If nothing is keyboard focused, expect context menu to not open",
+                mStripLayoutHelper.openKeyboardFocusedContextMenu());
+    }
+
+    @EnableFeatures({
+        ChromeFeatureList.ANDROID_KEYBOARD_A11Y,
+        ChromeFeatureList.TAB_STRIP_CONTEXT_MENU
+    })
+    @Test
+    public void testOpenContextMenu_tab() {
+        initializeTest(false, false, 0);
+        setupForIndividualTabContextMenu();
+        StripLayoutTab tabToFocus = mStripLayoutHelper.getStripLayoutTabsForTesting()[0];
+        tabToFocus.setKeyboardFocused(true);
+        assertTrue(
+                "Expected openKeyboardFocusedContextMenu to return true if tab context menu opened",
+                mStripLayoutHelper.openKeyboardFocusedContextMenu());
+        verify(mTabContextMenuCoordinator, times(1)).showMenu(any(), anyInt());
+    }
+
+    @EnableFeatures({
+        ChromeFeatureList.ANDROID_KEYBOARD_A11Y,
+        ChromeFeatureList.TAB_STRIP_CONTEXT_MENU
+    })
+    @Test
+    public void testOpenContextMenu_tabGroup() {
+        initializeTest(false, false, 0);
+        groupTabs(0, 1);
+        setupForGroupContextMenu();
+        StripLayoutGroupTitle groupTitle =
+                (StripLayoutGroupTitle) mStripLayoutHelper.getStripLayoutViewsForTesting()[0];
+        groupTitle.setKeyboardFocused(true);
+        assertTrue(
+                "Expected openKeyboardFocusedContextMenu to return true if tab context menu opened",
+                mStripLayoutHelper.openKeyboardFocusedContextMenu());
+        verify(mTabGroupContextMenuCoordinator, times(1)).showMenu(any(), any());
+    }
+
+    @EnableFeatures({
+        ChromeFeatureList.ANDROID_KEYBOARD_A11Y,
+        ChromeFeatureList.TAB_STRIP_CONTEXT_MENU
+    })
+    @Test
+    public void testOpenContextMenu_closeButton() {
+        initializeTest(false, false, 0);
+        // Set up a view for ListMenu to use (otherwise constructing the ListMenu will fail).
+        View tabView = new View(mActivity);
+        when(mModel.getTabAt(anyInt())).thenReturn(mTab);
+        when(mTab.getView()).thenReturn(tabView);
+        StripLayoutTab parentTab = mStripLayoutHelper.getStripLayoutTabsForTesting()[0];
+        parentTab.getCloseButton().setKeyboardFocused(true);
+        assertTrue(
+                "Expected openKeyboardFocusedContextMenu to return true if tab context menu opened",
+                mStripLayoutHelper.openKeyboardFocusedContextMenu());
+        assertTrue(
+                "Expected close button context menu to be showing",
+                mStripLayoutHelper.isCloseButtonMenuShowingForTesting());
+    }
+
     @Test
     @EnableFeatures({ChromeFeatureList.TABLET_TAB_STRIP_ANIMATION})
     public void testTabCreated_HorizontalAnimation() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java
index 2633ef952..add678f9 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsUnitTest.java
@@ -420,6 +420,28 @@
     }
 
     @Test
+    public void getInstanceCount_ExceedsLimit() {
+        when(mTabModelSelector.getModel(false)).thenReturn(mNormalTabModel);
+        when(mTabModelSelector.getModel(true)).thenReturn(mIncognitoTabModel);
+        int maxInstances = 3;
+        MultiWindowUtils.setMaxInstancesForTesting(maxInstances);
+
+        // Simulate persistence of instance state for max instances = 3.
+        writeInstanceInfo(
+                INSTANCE_ID_0, URL_1, /* tabCount= */ 3, /* incognitoTabCount= */ 2, TASK_ID_5);
+        writeInstanceInfo(
+                INSTANCE_ID_1, URL_2, /* tabCount= */ 0, /* incognitoTabCount= */ 0, TASK_ID_6);
+        writeInstanceInfo(
+                INSTANCE_ID_2, URL_3, /* tabCount= */ 6, /* incognitoTabCount= */ 2, TASK_ID_7);
+
+        // Simulate downgrade of instance limit.
+        MultiWindowUtils.setMaxInstancesForTesting(maxInstances - 1);
+
+        // Verify instance count.
+        assertEquals(3, MultiWindowUtils.getInstanceCount());
+    }
+
+    @Test
     @Config(sdk = 31)
     public void testGetInstanceIdForViewIntent_LessThanMaxInstancesOpen() {
         MultiWindowTestUtils.enableMultiInstance();
diff --git a/chrome/browser/actor/tools/page_tool.cc b/chrome/browser/actor/tools/page_tool.cc
index 6520934a..810a401 100644
--- a/chrome/browser/actor/tools/page_tool.cc
+++ b/chrome/browser/actor/tools/page_tool.cc
@@ -4,12 +4,17 @@
 
 #include "chrome/browser/actor/tools/page_tool.h"
 
+#include "base/functional/bind.h"
+#include "base/task/sequenced_task_runner.h"
 #include "chrome/browser/actor/actor_coordinator.h"
 #include "chrome/browser/actor/aggregated_journal.h"
 #include "chrome/common/actor/action_result.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
 #include "components/optimization_guide/proto/features/actions_data.pb.h"
+#include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "third_party/abseil-cpp/absl/strings/str_format.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
@@ -17,7 +22,10 @@
 
 namespace {
 
+using ::content::GlobalRenderFrameHostId;
 using ::content::RenderFrameHost;
+using ::content::WebContents;
+using ::content::WebContentsObserver;
 using ::optimization_guide::proto::ActionInformation;
 using ::optimization_guide::proto::ActionTarget;
 using ::optimization_guide::proto::ClickAction_ClickCount;
@@ -179,11 +187,36 @@
 
 namespace actor {
 
+// Observer to track if the a given RenderFrameHost is changed.
+class RenderFrameChangeObserver : public WebContentsObserver {
+ public:
+  RenderFrameChangeObserver(RenderFrameHost& rfh, base::OnceClosure callback)
+      : WebContentsObserver(WebContents::FromRenderFrameHost(&rfh)),
+        rfh_id_(rfh.GetGlobalId()),
+        callback_(std::move(callback)) {}
+
+  // WebContentsObserver
+  void RenderFrameHostChanged(RenderFrameHost* old_host,
+                              RenderFrameHost* /*new_host*/) override {
+    if (!callback_) {
+      return;
+    }
+
+    if (old_host && old_host->GetGlobalId() == rfh_id_) {
+      std::move(callback_).Run();
+    }
+  }
+
+ private:
+  GlobalRenderFrameHostId rfh_id_;
+  base::OnceClosure callback_;
+};
+
 PageTool::PageTool(AggregatedJournal& journal,
                    RenderFrameHost& frame,
                    const ActionInformation& action_information)
-    : action_information_(action_information) {
-  frame.GetRemoteAssociatedInterfaces()->GetInterface(&chrome_render_frame_);
+    : render_frame_host_(frame.GetWeakDocumentPtr()),
+      action_information_(action_information) {
   journal.EnsureJournalBound(frame);
 }
 
@@ -196,14 +229,20 @@
 }
 
 void PageTool::Invoke(InvokeCallback callback) {
+  invoke_callback_ = std::move(callback);
+  RenderFrameHost* frame = render_frame_host_.AsRenderFrameHostIfValid();
+  if (!frame) {
+    PostFinishInvoke(mojom::ActionResultCode::kFrameWentAway);
+    return;
+  }
+
   auto request = actor::mojom::ToolInvocation::New();
 
   switch (action_information_.action_info_case()) {
     case ActionInformation::ActionInfoCase::kClick: {
       auto click = mojom::ClickAction::New();
       if (!SetClickToolArgs(click, action_information_)) {
-        std::move(callback).Run(
-            MakeResult(mojom::ActionResultCode::kArgumentsInvalid));
+        PostFinishInvoke(mojom::ActionResultCode::kArgumentsInvalid);
         return;
       }
       request->action = mojom::ToolAction::NewClick(std::move(click));
@@ -212,8 +251,7 @@
     case ActionInformation::ActionInfoCase::kType: {
       auto type = mojom::TypeAction::New();
       if (!SetTypeToolArgs(type, action_information_)) {
-        std::move(callback).Run(
-            MakeResult(mojom::ActionResultCode::kArgumentsInvalid));
+        PostFinishInvoke(mojom::ActionResultCode::kArgumentsInvalid);
         return;
       }
       request->action = mojom::ToolAction::NewType(std::move(type));
@@ -222,8 +260,7 @@
     case ActionInformation::ActionInfoCase::kScroll: {
       auto scroll = mojom::ScrollAction::New();
       if (!SetScrollToolArgs(scroll, action_information_)) {
-        std::move(callback).Run(
-            MakeResult(mojom::ActionResultCode::kArgumentsInvalid));
+        PostFinishInvoke(mojom::ActionResultCode::kArgumentsInvalid);
         return;
       }
       request->action = mojom::ToolAction::NewScroll(std::move(scroll));
@@ -256,7 +293,35 @@
       NOTREACHED();
   }
 
-  chrome_render_frame_->InvokeTool(std::move(request), std::move(callback));
+  frame->GetRemoteAssociatedInterfaces()->GetInterface(&chrome_render_frame_);
+
+  // Watch for the RenderFrameHost being swapped out by a navigation (e.g. after
+  // clicking on a link). In that case, finish the invocation successfully as
+  // the ToolController will wait on the new page to load if needed. We rely on
+  // this running before the RenderFrameHost is destroyed since otherwise the
+  // chrome_render_frame_ mojo pipe will call the disconnect error handler which
+  // finishes the invocation with an error. Finally, this also handles cases
+  // where the old frame is put into the BFCache since in that case we may not
+  // get a reply from the renderer at all.
+  // Note: If there's already an in progress navigation then
+  // frame_change_observer may call FinishInvoke as a result of that navigation
+  // rather than the tool use. In that case we'll return success as if the tool
+  // completed successfully (expecting that's fine, as a new observation will be
+  // taken).
+  // `this` Unretained because the observer is owned by this class and thus
+  // removed on destruction.
+  frame_change_observer_ = std::make_unique<RenderFrameChangeObserver>(
+      *frame, base::BindOnce(&PageTool::FinishInvoke, base::Unretained(this),
+                             MakeOkResult()));
+
+  // `this` Unretained because this class owns the mojo pipe that invokes the
+  // callbacks.
+  chrome_render_frame_.set_disconnect_handler(
+      base::BindOnce(&PageTool::FinishInvoke, base::Unretained(this),
+                     MakeResult(mojom::ActionResultCode::kExecutorDestroyed)));
+  chrome_render_frame_->InvokeTool(
+      std::move(request),
+      base::BindOnce(&PageTool::FinishInvoke, base::Unretained(this)));
 }
 
 std::string PageTool::DebugString() const {
@@ -293,4 +358,22 @@
   }
 }
 
+void PageTool::FinishInvoke(mojom::ActionResultPtr result) {
+  if (!invoke_callback_) {
+    return;
+  }
+
+  std::move(invoke_callback_).Run(std::move(result));
+
+  frame_change_observer_.reset();
+}
+
+void PageTool::PostFinishInvoke(mojom::ActionResultCode result_code) {
+  CHECK(invoke_callback_);
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PageTool::FinishInvoke, weak_ptr_factory_.GetWeakPtr(),
+                     MakeResult(result_code)));
+}
+
 }  // namespace actor
diff --git a/chrome/browser/actor/tools/page_tool.h b/chrome/browser/actor/tools/page_tool.h
index cbf86a6..194f4ac 100644
--- a/chrome/browser/actor/tools/page_tool.h
+++ b/chrome/browser/actor/tools/page_tool.h
@@ -5,9 +5,14 @@
 #ifndef CHROME_BROWSER_ACTOR_TOOLS_PAGE_TOOL_H_
 #define CHROME_BROWSER_ACTOR_TOOLS_PAGE_TOOL_H_
 
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/actor/tools/tool.h"
+#include "chrome/common/actor.mojom-forward.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
 #include "components/optimization_guide/proto/features/actions_data.pb.h"
+#include "content/public/browser/weak_document_ptr.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 
 namespace content {
@@ -17,6 +22,7 @@
 namespace actor {
 
 class AggregatedJournal;
+class RenderFrameChangeObserver;
 
 // A page tool is any tool implemented in the renderer by ToolExecutor. This
 // class is shared by multiple tools and serves to implement the mojo shuttling
@@ -36,8 +42,17 @@
   std::string JournalEvent() const override;
 
  private:
+  void FinishInvoke(mojom::ActionResultPtr result);
+
+  void PostFinishInvoke(mojom::ActionResultCode result_code);
+
+  InvokeCallback invoke_callback_;
+  content::WeakDocumentPtr render_frame_host_;
+  std::unique_ptr<RenderFrameChangeObserver> frame_change_observer_;
   optimization_guide::proto::ActionInformation action_information_;
   mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame> chrome_render_frame_;
+
+  base::WeakPtrFactory<PageTool> weak_ptr_factory_{this};
 };
 
 }  // namespace actor
diff --git a/chrome/browser/actor/tools/tools_browsertest.cc b/chrome/browser/actor/tools/tools_browsertest.cc
index 8501be897..33b7416 100644
--- a/chrome/browser/actor/tools/tools_browsertest.cc
+++ b/chrome/browser/actor/tools/tools_browsertest.cc
@@ -2217,6 +2217,72 @@
   ExpectErrorResult(result, mojom::ActionResultCode::kFrameWentAway);
 }
 
+// Ensure a page tool (click, in this case) causing a cross-document navigation
+// works successfully.
+IN_PROC_BROWSER_TEST_F(ActorToolsTest, PageToolNavigates) {
+  const GURL url_start =
+      embedded_test_server()->GetURL("/actor/cross_document_nav.html");
+  const GURL url_next =
+      embedded_test_server()->GetURL("/actor/simple_iframe.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url_start));
+
+  // Send a click to the link.
+  std::optional<int> link_id = GetDOMNodeId(*main_frame(), "#link");
+  ASSERT_TRUE(link_id);
+
+  // TODO(crbug.com/414662842): Add cases where the new document load is delayed
+  // as well as the PageTool doesn't outlive the outgoing RenderFrame
+
+  BrowserAction action = MakeClick(*main_frame(), link_id.value());
+  TestFuture<mojom::ActionResultPtr> result;
+
+  // TODO(crbug.com/414662842): The TestNavigationManager can be removed once
+  // PageTools observe loading navigations.
+  TestNavigationManager main_manager(web_contents(), url_next);
+  actor_coordinator().Act(action, result.GetCallback());
+  ASSERT_TRUE(main_manager.WaitForNavigationFinished());
+
+  ExpectOkResult(result);
+
+  EXPECT_EQ(web_contents()->GetURL(), url_next);
+}
+
+// Ensure a page tool (click, in this case) causing a cross-document navigation
+// works successfully in cross-site cases (where a new RenderFrameHost is
+// guaranteed).
+IN_PROC_BROWSER_TEST_F(ActorToolsTest, PageToolNavigatesCrossSite) {
+  const GURL url_start = embedded_https_test_server().GetURL(
+      "foo.com", "/actor/cross_document_nav.html");
+  const GURL url_next = embedded_https_test_server().GetURL(
+      "bar.com", "/actor/simple_iframe.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url_start));
+
+  // The link in the file is relative so replace it to include the mock
+  // hostname.
+  ASSERT_TRUE(
+      ExecJs(web_contents(),
+             JsReplace("document.getElementById('link').href = $1", url_next)));
+
+  // Send a click to the link.
+  std::optional<int> link_id = GetDOMNodeId(*main_frame(), "#link");
+  ASSERT_TRUE(link_id);
+
+  // TODO(crbug.com/414662842): Add cases where the new document load is delayed
+  // as well as the PageTool doesn't outlive the outgoing RenderFrame
+
+  BrowserAction action = MakeClick(*main_frame(), link_id.value());
+  TestFuture<mojom::ActionResultPtr> result;
+
+  // TODO(crbug.com/414662842): The TestNavigationManager can be removed once
+  // PageTools observe loading navigations.
+  TestNavigationManager main_manager(web_contents(), url_next);
+  actor_coordinator().Act(action, result.GetCallback());
+  ASSERT_TRUE(main_manager.WaitForNavigationFinished());
+
+  ExpectOkResult(result);
+
+  EXPECT_EQ(web_contents()->GetURL(), url_next);
+}
 }  // namespace
 
 }  // namespace actor
diff --git a/chrome/browser/ai/ai_data_keyed_service.cc b/chrome/browser/ai/ai_data_keyed_service.cc
index 03b4514..f7ad664 100644
--- a/chrome/browser/ai/ai_data_keyed_service.cc
+++ b/chrome/browser/ai/ai_data_keyed_service.cc
@@ -28,7 +28,7 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/unguessable_token.h"
 #include "chrome/browser/content_extraction/inner_text.h"
 #include "chrome/browser/history_embeddings/history_embeddings_service_factory.h"
diff --git a/chrome/browser/ai/ai_language_model.cc b/chrome/browser/ai/ai_language_model.cc
index d22c38b6..3d1915d14 100644
--- a/chrome/browser/ai/ai_language_model.cc
+++ b/chrome/browser/ai/ai_language_model.cc
@@ -15,6 +15,7 @@
 #include "base/notimplemented.h"
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/types/expected.h"
 #include "chrome/browser/ai/ai_context_bound_object.h"
 #include "chrome/browser/ai/ai_manager.h"
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index b7564fc..a472177 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -235,6 +235,9 @@
 // Attempts restoring a previous session if there is one. Otherwise, opens
 // either the profile picker or a new browser, depending on user preferences.
 void AttemptSessionRestore(Profile* profile) {
+  if (!profile) {
+    return;
+  }
   DCHECK(!profile->IsGuestSession());
   DCHECK(!IsProfileSignedOut(profile->GetPath()));
   SessionService* sessionService =
diff --git a/chrome/browser/ash/arc/tracing/arc_tracing_event_matcher.h b/chrome/browser/ash/arc/tracing/arc_tracing_event_matcher.h
index d39c64a6..49f94e7 100644
--- a/chrome/browser/ash/arc/tracing/arc_tracing_event_matcher.h
+++ b/chrome/browser/ash/arc/tracing/arc_tracing_event_matcher.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_ASH_ARC_TRACING_ARC_TRACING_EVENT_MATCHER_H_
 
 #include <stddef.h>
+#include <stdint.h>
 
 #include <map>
 #include <optional>
diff --git a/chrome/browser/ash/crostini/crostini_export_import_status_tracker.h b/chrome/browser/ash/crostini/crostini_export_import_status_tracker.h
index 1907a20a..3f0dad7e 100644
--- a/chrome/browser/ash/crostini/crostini_export_import_status_tracker.h
+++ b/chrome/browser/ash/crostini/crostini_export_import_status_tracker.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_ASH_CROSTINI_CROSTINI_EXPORT_IMPORT_STATUS_TRACKER_H_
 #define CHROME_BROWSER_ASH_CROSTINI_CROSTINI_EXPORT_IMPORT_STATUS_TRACKER_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <string>
 
diff --git a/chrome/browser/ash/extensions/file_manager/private_api_strings.cc b/chrome/browser/ash/extensions/file_manager/private_api_strings.cc
index ee82c33..9a6a49a 100644
--- a/chrome/browser/ash/extensions/file_manager/private_api_strings.cc
+++ b/chrome/browser/ash/extensions/file_manager/private_api_strings.cc
@@ -7,8 +7,10 @@
 #include <memory>
 #include <utility>
 
+#include "base/check_deref.h"
 #include "base/values.h"
 #include "chrome/browser/ash/file_manager/file_manager_string_util.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "extensions/common/extension_l10n_util.h"
 
@@ -21,11 +23,16 @@
     default;
 
 ExtensionFunction::ResponseAction FileManagerPrivateGetStringsFunction::Run() {
-  base::Value::Dict dict = GetFileManagerStrings();
+  // TODO(crbug.com/404131876): Remove g_browser_process usage.
+  const std::string& application_locale =
+      g_browser_process->GetApplicationLocale();
+  base::Value::Dict dict = GetFileManagerStrings(application_locale);
 
   const std::string locale = extension_l10n_util::CurrentLocaleOrDefault();
   AddFileManagerFeatureStrings(
-      locale, Profile::FromBrowserContext(browser_context()), &dict);
+      locale, application_locale,
+      CHECK_DEREF(g_browser_process->variations_service()),
+      Profile::FromBrowserContext(browser_context()), &dict);
 
   return RespondNow(WithArguments(std::move(dict)));
 }
diff --git a/chrome/browser/ash/file_manager/BUILD.gn b/chrome/browser/ash/file_manager/BUILD.gn
index ab65183..4fe0b0b 100644
--- a/chrome/browser/ash/file_manager/BUILD.gn
+++ b/chrome/browser/ash/file_manager/BUILD.gn
@@ -140,7 +140,6 @@
     "//ash/webui/file_manager:constants",
     "//base:i18n",
     "//chrome/app:generated_resources",
-    "//chrome/browser:browser_process",
     "//chrome/browser/apps/app_service:constants",
     "//chrome/browser/ash/app_list",
     "//chrome/browser/ash/app_list/search",
@@ -171,6 +170,7 @@
     "//chromeos/ash/components/browser_context_helper",
     "//chromeos/ash/components/drivefs",
     "//chromeos/ash/components/file_manager:constants",
+    "//chromeos/ash/components/install_attributes",
     "//chromeos/ash/components/policy",
     "//chromeos/ash/experiences/arc:arc_base_utils",
     "//chromeos/ash/experiences/arc:arc_features",
@@ -179,6 +179,7 @@
     "//chromeos/components/disks:prefs",
     "//chromeos/constants",
     "//chromeos/dbus/power",
+    "//components/application_locale_storage",
     "//components/download/public/common:public",
     "//components/pref_registry",
     "//components/services/app_service/public/cpp:app_types",
@@ -428,6 +429,7 @@
     "//build/config/coverage:buildflags",
     "//chrome/browser",
     "//chrome/browser:browser_process",
+    "//chrome/browser:global_features",
     "//chrome/browser/apps/app_service",
     "//chrome/browser/apps/app_service:app_registry_cache_waiter",
     "//chrome/browser/apps/app_service:constants",
@@ -502,6 +504,7 @@
     "//chromeos/dbus/dlp",
     "//chromeos/dbus/dlp:dlp_proto",
     "//components/account_id",
+    "//components/application_locale_storage",
     "//components/download/public/common:public",
     "//components/drive",
     "//components/file_access:test_support",
diff --git a/chrome/browser/ash/file_manager/DEPS b/chrome/browser/ash/file_manager/DEPS
index 41435e0..ce0227b0 100644
--- a/chrome/browser/ash/file_manager/DEPS
+++ b/chrome/browser/ash/file_manager/DEPS
@@ -39,8 +39,6 @@
   "+chrome/browser/ash/smb_client",
   "+chrome/browser/ash/system",
   "+chrome/browser/ash/system_web_apps",
-  "+chrome/browser/browser_process.h",
-  "+chrome/browser/browser_process_platform_part_ash.h",
   "+chrome/browser/chromeos/policy/dlp",
   "+chrome/browser/chromeos/upload_office_to_cloud",
   "+chrome/browser/download",
@@ -89,6 +87,8 @@
 specific_include_rules = {
   ".*test.*\..*": [
     "+chrome/browser/ash/login/test",
+    "+chrome/browser/browser_process.h",
+    "+chrome/browser/global_features.h",
     "+chrome/browser/extensions/chrome_test_extension_loader.h",
     "+chrome/browser/extensions/extension_apitest.h",
     "+chrome/browser/extensions/mixin_based_extension_apitest.h",
diff --git a/chrome/browser/ash/file_manager/file_manager_jstest.cc b/chrome/browser/ash/file_manager/file_manager_jstest.cc
index 9617bd635..9d103f2 100644
--- a/chrome/browser/ash/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_jstest.cc
@@ -431,6 +431,10 @@
   RunTestURL("common/js/entry_utils_unittest.js");
 }
 
+IN_PROC_BROWSER_TEST_F(FileManagerJsTest, DialogActionController) {
+  RunTestURL("foreground/js/dialog_action_controller_unittest.js");
+}
+
 // Rerun some of the tests above, using CrosComponents.
 class FileManagerJsCrosComponentsTest : public FileManagerJsTest {
  public:
diff --git a/chrome/browser/ash/file_manager/file_manager_jstest_base.cc b/chrome/browser/ash/file_manager/file_manager_jstest_base.cc
index dca335f..ce4607d8 100644
--- a/chrome/browser/ash/file_manager/file_manager_jstest_base.cc
+++ b/chrome/browser/ash/file_manager/file_manager_jstest_base.cc
@@ -8,14 +8,18 @@
 #include "ash/webui/file_manager/resource_loader.h"
 #include "ash/webui/file_manager/resources/grit/file_manager_swa_resources_map.h"
 #include "ash/webui/file_manager/url_constants.h"
+#include "base/check_deref.h"
 #include "base/lazy_instance.h"
 #include "base/path_service.h"
 #include "chrome/browser/ash/file_manager/file_manager_string_util.h"
 #include "chrome/browser/ash/file_manager/file_manager_test_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/global_features.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/test_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/application_locale_storage/application_locale_storage.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "content/public/browser/web_ui_data_source.h"
@@ -57,8 +61,13 @@
     ash::file_manager::AddFilesAppResources(files_swa_source,
                                             kFileManagerGenResources);
 
-    dict_ = GetFileManagerStrings();
-    AddFileManagerFeatureStrings("en-US", Profile::FromWebUI(web_ui), &dict_);
+    const std::string& application_locale =
+        g_browser_process->GetFeatures()->application_locale_storage()->Get();
+    dict_ = GetFileManagerStrings(application_locale);
+    AddFileManagerFeatureStrings(
+        "en-US", application_locale,
+        CHECK_DEREF(g_browser_process->variations_service()),
+        Profile::FromWebUI(web_ui), &dict_);
     files_swa_source->AddLocalizedStrings(dict_);
     files_swa_source->UseStringsJs();
 
diff --git a/chrome/browser/ash/file_manager/file_manager_string_util.cc b/chrome/browser/ash/file_manager/file_manager_string_util.cc
index 6c1082e..9ae1575e 100644
--- a/chrome/browser/ash/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/ash/file_manager/file_manager_string_util.cc
@@ -27,8 +27,6 @@
 #include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/browser_process_platform_part_ash.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_policy_constants.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
@@ -36,6 +34,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/ash/components/install_attributes/install_attributes.h"
 #include "chromeos/ash/experiences/arc/arc_features.h"
 #include "chromeos/ash/experiences/arc/arc_util.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -1185,12 +1184,12 @@
   return ash::ProfileHelper::Get()->GetProfileByUser(user);
 }
 
-bool IsEligibleAndEnabledGoogleOneOfferFilesBanner() {
+bool IsEligibleAndEnabledGoogleOneOfferFilesBanner(
+    const std::string& application_locale,
+    const variations::VariationsService& variations_service) {
   // Google One offer is for a device, not for an account. Do not show a banner
   // if a device is enrolled.
-  if (g_browser_process->platform_part()
-          ->browser_policy_connector_ash()
-          ->IsDeviceEnterpriseManaged()) {
+  if (ash::InstallAttributes::Get()->IsEnterpriseManaged()) {
     return false;
   }
 
@@ -1215,14 +1214,12 @@
     return false;
   }
 
-  if (!kGoogleOneOfferBannerSupportedLocales.contains(
-          g_browser_process->GetApplicationLocale())) {
+  if (!kGoogleOneOfferBannerSupportedLocales.contains(application_locale)) {
     return false;
   }
 
   if (!kGoogleOneOfferBannerSupportedCountries.contains(
-          g_browser_process->variations_service()
-              ->GetStoredPermanentCountry())) {
+          variations_service.GetStoredPermanentCountry())) {
     return false;
   }
 
@@ -1290,7 +1287,7 @@
 
 }  // namespace
 
-base::Value::Dict GetFileManagerStrings() {
+base::Value::Dict GetFileManagerStrings(const std::string& application_locale) {
   base::Value::Dict dict;
 
   AddStringsForDrive(&dict);
@@ -1326,8 +1323,7 @@
            base::StringPrintf(kHelpURLFormat, kNoActionForFileHelpNumber));
   dict.Set("DLP_HELP_URL", policy::dlp::kDlpLearnMoreUrl);
 
-  webui::SetLoadTimeDataDefaults(g_browser_process->GetApplicationLocale(),
-                                 &dict);
+  webui::SetLoadTimeDataDefaults(application_locale, &dict);
 
   return dict;
 }
@@ -1368,9 +1364,12 @@
   return fmod(local_day_of_week - (day_of_week - 1) + 7, 7);
 }
 
-void AddFileManagerFeatureStrings(const std::string& locale,
-                                  Profile* profile,
-                                  base::Value::Dict* dict) {
+void AddFileManagerFeatureStrings(
+    const std::string& ui_locale,
+    const std::string& application_locale,
+    const variations::VariationsService& variations_service,
+    Profile* profile,
+    base::Value::Dict* dict) {
   DCHECK(profile);
 
   dict->Set("HIDE_SPACE_INFO", ash::DemoSession::IsDeviceInDemoMode());
@@ -1424,11 +1423,12 @@
   dict->Set("VMS_FOR_SHARING", std::move(vms));
 
   // Lastly, set UI_LOCALE and locale-dependent settings.
-  dict->Set("UI_LOCALE", locale);
+  dict->Set("UI_LOCALE", ui_locale);
   dict->Set("WEEK_START_FROM", GetLocaleBasedWeekStart());
 
   // ELIGIBLE_AND_ENABLED_GOOGLE_ONE_OFFER_FILES_BANNER does additional checks
   // in addition to a feature flag check.
   dict->Set("ELIGIBLE_AND_ENABLED_GOOGLE_ONE_OFFER_FILES_BANNER",
-            IsEligibleAndEnabledGoogleOneOfferFilesBanner());
+            IsEligibleAndEnabledGoogleOneOfferFilesBanner(application_locale,
+                                                          variations_service));
 }
diff --git a/chrome/browser/ash/file_manager/file_manager_string_util.h b/chrome/browser/ash/file_manager/file_manager_string_util.h
index c01145e5..fcad23d1 100644
--- a/chrome/browser/ash/file_manager/file_manager_string_util.h
+++ b/chrome/browser/ash/file_manager/file_manager_string_util.h
@@ -11,7 +11,13 @@
 
 class Profile;
 
-base::Value::Dict GetFileManagerStrings();
+namespace variations {
+class VariationsService;
+}  // namespace variations
+
+// `application_locale` should be the locale associated with
+// `g_browser_process`.
+base::Value::Dict GetFileManagerStrings(const std::string& application_locale);
 
 base::Value::Dict GetFileManagerPluralStrings();
 
@@ -19,8 +25,13 @@
 // to indicate which day is the start of week based on the current locale.
 int GetLocaleBasedWeekStart();
 
-void AddFileManagerFeatureStrings(const std::string& locale,
-                                  Profile* profile,
-                                  base::Value::Dict* dict);
+// `application_locale` should be the locale associated with
+// `g_browser_process`.
+void AddFileManagerFeatureStrings(
+    const std::string& ui_locale,
+    const std::string& application_locale,
+    const variations::VariationsService& variations_service,
+    Profile* profile,
+    base::Value::Dict* dict);
 
 #endif  // CHROME_BROWSER_ASH_FILE_MANAGER_FILE_MANAGER_STRING_UTIL_H_
diff --git a/chrome/browser/ash/floating_sso/cookie_sync_conversions.h b/chrome/browser/ash/floating_sso/cookie_sync_conversions.h
index 5e015ac..23cd923 100644
--- a/chrome/browser/ash/floating_sso/cookie_sync_conversions.h
+++ b/chrome/browser/ash/floating_sso/cookie_sync_conversions.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 #include <optional>
+#include <string>
 
 namespace base {
 class Time;
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/fido_authentication_message_helper.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/fido_authentication_message_helper.h
index 9a3c09f..308abe9 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/fido_authentication_message_helper.h
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/fido_authentication_message_helper.h
@@ -5,7 +5,10 @@
 #ifndef CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_CONNECTIVITY_FIDO_AUTHENTICATION_MESSAGE_HELPER_H_
 #define CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_CONNECTIVITY_FIDO_AUTHENTICATION_MESSAGE_HELPER_H_
 
+#include <stdint.h>
+
 #include <vector>
+
 namespace ash::quick_start::message_helper {
 
 inline constexpr char kCredentialIdKey[] = "id";
diff --git a/chrome/browser/ash/power/auto_screen_brightness/monotone_cubic_spline.h b/chrome/browser/ash/power/auto_screen_brightness/monotone_cubic_spline.h
index efb03af..cbbfb1a 100644
--- a/chrome/browser/ash/power/auto_screen_brightness/monotone_cubic_spline.h
+++ b/chrome/browser/ash/power/auto_screen_brightness/monotone_cubic_spline.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_ASH_POWER_AUTO_SCREEN_BRIGHTNESS_MONOTONE_CUBIC_SPLINE_H_
 
 #include <optional>
+#include <string>
 #include <vector>
 
 namespace ash {
diff --git a/chrome/browser/ash/system_web_apps/apps/chrome_file_manager_ui_delegate.cc b/chrome/browser/ash/system_web_apps/apps/chrome_file_manager_ui_delegate.cc
index 5dfefba6..4957c53 100644
--- a/chrome/browser/ash/system_web_apps/apps/chrome_file_manager_ui_delegate.cc
+++ b/chrome/browser/ash/system_web_apps/apps/chrome_file_manager_ui_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/system_web_apps/apps/chrome_file_manager_ui_delegate.h"
 
+#include "base/check_deref.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
@@ -31,10 +32,11 @@
 ChromeFileManagerUIDelegate::~ChromeFileManagerUIDelegate() = default;
 
 base::Value::Dict ChromeFileManagerUIDelegate::GetLoadTimeData() const {
-  base::Value::Dict dict = GetFileManagerStrings();
-
-  const std::string locale = g_browser_process->GetApplicationLocale();
-  AddFileManagerFeatureStrings(locale, Profile::FromWebUI(web_ui_), &dict);
+  const std::string& locale = g_browser_process->GetApplicationLocale();
+  base::Value::Dict dict = GetFileManagerStrings(locale);
+  AddFileManagerFeatureStrings(
+      locale, locale, CHECK_DEREF(g_browser_process->variations_service()),
+      Profile::FromWebUI(web_ui_), &dict);
   return dict;
 }
 
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 6c9b64cc..ff248ba 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -46,7 +46,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/expected.h"
 #include "base/version.h"
 #include "base/win/elevation_util.h"
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/routines/diagnostic_routine_converters.h b/chrome/browser/chromeos/extensions/telemetry/api/routines/diagnostic_routine_converters.h
index b6397c1..e35d093 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/routines/diagnostic_routine_converters.h
+++ b/chrome/browser/chromeos/extensions/telemetry/api/routines/diagnostic_routine_converters.h
@@ -116,9 +116,8 @@
     crosapi::mojom::TelemetryDiagnosticCameraSubtestResult input);
 
 template <class InputT,
-          class OutputT = decltype(Convert(std::declval<InputT>())),
-          class = std::enable_if_t<std::is_enum_v<InputT> ||
-                                   std::is_integral_v<InputT>>>
+          class OutputT = decltype(Convert(std::declval<InputT>()))>
+  requires(std::is_enum_v<InputT> || std::is_integral_v<InputT>)
 std::vector<OutputT> ConvertVector(std::vector<InputT> input) {
   std::vector<OutputT> output;
   for (auto elem : input) {
@@ -131,8 +130,8 @@
           class... Types,
           class OutputT = decltype(unchecked::UncheckedConvertPtr(
               std::declval<InputT>(),
-              std::declval<Types>()...)),
-          class = std::enable_if_t<std::is_default_constructible_v<OutputT>>>
+              std::declval<Types>()...))>
+  requires(std::is_default_constructible_v<OutputT>)
 OutputT ConvertPtr(InputT input, Types... args) {
   return (input) ? unchecked::UncheckedConvertPtr(std::move(input), args...)
                  : OutputT();
diff --git a/chrome/browser/device_identity/chromeos/token_encryptor.h b/chrome/browser/device_identity/chromeos/token_encryptor.h
index 1e8a11c..4e11752 100644
--- a/chrome/browser/device_identity/chromeos/token_encryptor.h
+++ b/chrome/browser/device_identity/chromeos/token_encryptor.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_DEVICE_IDENTITY_CHROMEOS_TOKEN_ENCRYPTOR_H_
 #define CHROME_BROWSER_DEVICE_IDENTITY_CHROMEOS_TOKEN_ENCRYPTOR_H_
 
+#include <stdint.h>
+
 #include <array>
 #include <memory>
 #include <string>
diff --git a/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win_unittest.cc b/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win_unittest.cc
index 37db34e..f885dfc 100644
--- a/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win_unittest.cc
+++ b/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
diff --git a/chrome/browser/enterprise/signin/oidc_managed_profile_creation_delegate_unittest.cc b/chrome/browser/enterprise/signin/oidc_managed_profile_creation_delegate_unittest.cc
index 8019f82..77567e1 100644
--- a/chrome/browser/enterprise/signin/oidc_managed_profile_creation_delegate_unittest.cc
+++ b/chrome/browser/enterprise/signin/oidc_managed_profile_creation_delegate_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/enterprise/signin/enterprise_signin_prefs.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
index e1dae7c..6e0060cde 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
@@ -68,6 +68,12 @@
   }
 
  protected:
+  std::vector<tabs::TabAlert> GetTabAlertStatesForContents(
+      content::WebContents* web_contents) {
+    return GetTabAlertStatesForTab(
+        tabs::TabInterface::GetFromContents(web_contents));
+  }
+
   void SimulateMouseClickInCurrentTab() {
     content::SimulateMouseClick(
         browser()->tab_strip_model()->GetActiveWebContents(), 0,
diff --git a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
index 66b7a85..14444f5 100644
--- a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
+++ b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
@@ -128,9 +128,13 @@
   // ResourceBundle and g_browser_process are not always initialized in unit
   // tests.
   if (ui::ResourceBundle::HasSharedInstance() && g_browser_process) {
+    // TODO(crbug.com/404131876): Remove g_browser_process usage.
+    const std::string& application_locale =
+        g_browser_process->GetApplicationLocale();
+
     ui::TemplateReplacements file_manager_replacements;
-    ui::TemplateReplacementsFromDictionaryValue(GetFileManagerStrings(),
-                                                &file_manager_replacements);
+    ui::TemplateReplacementsFromDictionaryValue(
+        GetFileManagerStrings(application_locale), &file_manager_replacements);
     template_replacements_[extension_misc::kFilesManagerAppId] =
         std::move(file_manager_replacements);
   }
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 7eb8a16..a9c4424 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -350,27 +350,7 @@
 
   LoadExtensionsFromCommandLineFlag(switches::kDisableExtensionsExcept);
   if (load_command_line_extensions) {
-    bool command_line_blocked = true;
-    if (base::FeatureList::IsEnabled(
-            extensions_features::kDisableLoadExtensionCommandLineSwitch)) {
-      LOG(WARNING)
-          << "--load-extension is not allowed in Google Chrome, ignoring.";
-    } else if (safe_browsing::IsEnhancedProtectionEnabled(
-                   *profile_->GetPrefs())) {
-      VLOG(1) << "--load-extension is not allowed for users opted into "
-              << "Enhanced Safe Browsing, ignoring.";
-    } else if (ShouldBlockCommandLineExtension(*profile_)) {
-      // TODO(crbug.com/401529219): Deprecate this restriction once
-      // --load-extension switch is restricted on Chrome builds.
-      VLOG(1)
-          << "--load-extension is not allowed for users that have the policy "
-          << "ExtensionInstallTypeBlocklist::command_line, ignoring.";
-    } else {
-      LoadExtensionsFromCommandLineFlag(switches::kLoadExtension);
-      command_line_blocked = false;
-    }
-    base::UmaHistogramBoolean("Extensions.LoadingFromCommandLineBlocked",
-                              command_line_blocked);
+    LoadExtensionsFromCommandLineFlag(switches::kLoadExtension);
   }
   EnabledReloadableExtensions();
   delayed_install_manager_->FinishInstallationsDelayedByShutdown();
@@ -404,22 +384,58 @@
 
 void ExtensionService::LoadExtensionsFromCommandLineFlag(
     const char* switch_name) {
-  if (command_line_->HasSwitch(switch_name)) {
-    base::CommandLine::StringType path_list =
-        command_line_->GetSwitchValueNative(switch_name);
-    base::StringTokenizerT<base::CommandLine::StringType,
-                           base::CommandLine::StringType::const_iterator>
-        t(path_list, FILE_PATH_LITERAL(","));
-    while (t.GetNext()) {
-      std::string extension_id;
-      UnpackedInstaller::Create(profile_)->LoadFromCommandLine(
-          base::FilePath(t.token_piece()), &extension_id,
-          false /*only-allow-apps*/);
-      if (switch_name == switches::kDisableExtensionsExcept) {
-        extension_registrar_->AddDisableFlagExemptedExtension(extension_id);
-      }
+  CHECK(switch_name == switches::kLoadExtension ||
+        switch_name == switches::kDisableExtensionsExcept);
+  if (!command_line_->HasSwitch(switch_name)) {
+    return;
+  }
+
+  // Check that --load-extension is allowed.
+  // TODO(crbug.com/419530940): Apply restrictions to
+  // --disable-extensions-except switch once the feature is approved and
+  // implemented.
+  if (switch_name == switches::kLoadExtension) {
+    if (base::FeatureList::IsEnabled(
+            extensions_features::kDisableLoadExtensionCommandLineSwitch)) {
+      LOG(WARNING)
+          << "--load-extension is not allowed in Google Chrome, ignoring.";
+      return;
+    }
+    if (safe_browsing::IsEnhancedProtectionEnabled(*profile_->GetPrefs())) {
+      VLOG(1) << "--load-extension is not allowed for users opted into "
+              << "Enhanced Safe Browsing, ignoring.";
+      return;
+    }
+    if (ShouldBlockCommandLineExtension(*profile_)) {
+      // TODO(crbug.com/401529219): Deprecate this restriction once
+      // --load-extension removal on Chrome builds is fully launched.
+      VLOG(1)
+          << "--load-extension is not allowed for users that have the policy "
+          << "ExtensionInstallTypeBlocklist::command_line, ignoring.";
+      return;
     }
   }
+
+  base::CommandLine::StringType path_list =
+      command_line_->GetSwitchValueNative(switch_name);
+  base::StringTokenizerT<base::CommandLine::StringType,
+                         base::CommandLine::StringType::const_iterator>
+      t(path_list, FILE_PATH_LITERAL(","));
+  while (t.GetNext()) {
+    std::string extension_id;
+    UnpackedInstaller::Create(profile_)->LoadFromCommandLine(
+        base::FilePath(t.token_piece()), &extension_id,
+        /*only-allow-apps=*/false);
+    if (switch_name == switches::kDisableExtensionsExcept) {
+      extension_registrar_->AddDisableFlagExemptedExtension(extension_id);
+    }
+  }
+
+  base::UmaHistogramEnumeration(
+      "Extensions.LoadingFromCommandLine",
+      switch_name == switches::kLoadExtension
+          ? ExtensionService::LoadExtensionFlag::kLoadExtension
+          : ExtensionService::LoadExtensionFlag::kDisableExtensionsExcept);
 }
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 4c22473d..29d9cedf 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -234,6 +234,21 @@
   }
 #endif
 
+  // Load Extension Flags.
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  //
+  // LINT.IfChange(LoadExtensionFlag)
+  enum class LoadExtensionFlag {
+    // --load-extension flag.
+    kLoadExtension = 0,
+    // --disable-extensions-except flag.
+    kDisableExtensionsExcept = 1,
+
+    kMaxValue = kDisableExtensionsExcept,
+  };
+  // LINT.ThenChange(/tools/metrics/histograms/metadata/extensions/enums.xml:LoadExtensionFlag)
+
  private:
   // Loads extensions specified via a command line flag/switch.
   void LoadExtensionsFromCommandLineFlag(const char* switch_name);
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 836a9ed1..89e08a1 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -5966,6 +5966,7 @@
 // Tests that --load-extension is ignored for users opted in to Enhanced Safe
 // Browsing (ESB).
 TEST_F(ExtensionServiceTest, WillNotLoadFromCommandLineForESBUsers) {
+  base::HistogramTester histograms;
   InitializeEmptyExtensionServiceWithTestingPrefs();
   // Enable ESB.
   profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
@@ -5979,6 +5980,8 @@
   task_environment()->RunUntilIdle();
   ASSERT_EQ(0u, loaded_extensions().size());
   ValidatePrefKeyCount(0);
+
+  histograms.ExpectTotalCount("Extensions.LoadingFromCommandLine", 0);
 }
 
 // Tests --load-extension works for non-ESB users.
@@ -5999,14 +6002,16 @@
   ASSERT_EQ(1u, loaded_extensions().size());
   ValidatePrefKeyCount(1);
 
-  histograms.ExpectUniqueSample("Extensions.LoadingFromCommandLineBlocked",
-                                false, 1);
+  histograms.ExpectUniqueSample(
+      "Extensions.LoadingFromCommandLine",
+      ExtensionService::LoadExtensionFlag::kLoadExtension, 1);
 }
 
 // Tests that --load-extension is ignored for users with policy
 // ExtensionInstallTypeBlocklist containing command_line.
 TEST_F(ExtensionServiceTest,
        WillNotLoadFromCommandLineForUsersWithPolicyFalse) {
+  base::HistogramTester histograms;
   InitializeEmptyExtensionServiceWithTestingPrefs();
 
   profile()->GetPrefs()->SetList(pref_names::kExtensionInstallTypeBlocklist,
@@ -6021,11 +6026,15 @@
   task_environment()->RunUntilIdle();
   ASSERT_EQ(0u, loaded_extensions().size());
   ValidatePrefKeyCount(0);
+
+  histograms.ExpectTotalCount("Extensions.LoadingFromCommandLine", 0);
 }
 
-// Tests --load-extension works for users with policy
-// ExtensionInstallTypeBlocklist not containing "command_line" (default value)
+// Tests --load-extension and --disable-extensions-except work for users with
+// policy ExtensionInstallTypeBlocklist not containing "command_line" (default
+// value)
 TEST_F(ExtensionServiceTest, LoadsFromCommandLineForUsersWithoutPolicy) {
+  base::HistogramTester histograms;
   InitializeEmptyExtensionServiceWithTestingPrefs();
   // Not setting pref as false is default value.
   // Try to load an extension from command line.
@@ -6033,11 +6042,20 @@
       base::MakeAbsoluteFilePath(data_dir().AppendASCII("good_unpacked"));
   base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
       switches::kLoadExtension, path);
+  base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
+      switches::kDisableExtensionsExcept, path);
   service()->Init();
   task_environment()->RunUntilIdle();
   EXPECT_EQ(0u, GetErrors().size());
   ASSERT_EQ(1u, loaded_extensions().size());
   ValidatePrefKeyCount(1);
+
+  histograms.ExpectBucketCount(
+      "Extensions.LoadingFromCommandLine",
+      ExtensionService::LoadExtensionFlag::kLoadExtension, 1);
+  histograms.ExpectBucketCount(
+      "Extensions.LoadingFromCommandLine",
+      ExtensionService::LoadExtensionFlag::kDisableExtensionsExcept, 1);
 }
 
 TEST_F(ExtensionServiceTest, DisableLoadExtensionCommandLineSwitch) {
@@ -6063,8 +6081,7 @@
   ASSERT_EQ(0u, loaded_extensions().size());
   ValidatePrefKeyCount(0);
 
-  histograms.ExpectUniqueSample("Extensions.LoadingFromCommandLineBlocked",
-                                true, 1);
+  histograms.ExpectTotalCount("Extensions.LoadingFromCommandLine", 0);
 }
 
 // Tests that we generate IDs when they are not specified in the manifest for
diff --git a/chrome/browser/media/cast_mirroring_service_host_browsertest.cc b/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
index 092801c..53cb772 100644
--- a/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
+++ b/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
@@ -351,6 +351,12 @@
   // InProcessBrowserTest override.
   void SetUp() override { InProcessBrowserTest::SetUp(); }
 
+  std::vector<tabs::TabAlert> GetTabAlertStatesForContents(
+      content::WebContents* web_contents) {
+    return GetTabAlertStatesForTab(
+        tabs::TabInterface::GetFromContents(web_contents));
+  }
+
  private:
   // mojom::SessionObserver mocks.
   MOCK_METHOD(void, OnError, (mojom::SessionError));
diff --git a/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc b/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc
index 8e5683b..ca878fc 100644
--- a/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
diff --git a/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc b/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc
index 142ac61..11877d4 100644
--- a/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/hash/hash.h"
 #include "base/json/json_string_value_serializer.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_number_conversions.h"
diff --git a/chrome/browser/on_device_translation/language_pack_util.cc b/chrome/browser/on_device_translation/language_pack_util.cc
index acbed61..259fd5c 100644
--- a/chrome/browser/on_device_translation/language_pack_util.cc
+++ b/chrome/browser/on_device_translation/language_pack_util.cc
@@ -102,6 +102,24 @@
 
 }  // namespace
 
+bool IsPopularLanguage(SupportedLanguage supported_language) {
+  return supported_language == SupportedLanguage::kEn ||
+         supported_language == SupportedLanguage::kZh ||
+         supported_language == SupportedLanguage::kZhHant ||
+         supported_language == SupportedLanguage::kJa ||
+         supported_language == SupportedLanguage::kPt ||
+         supported_language == SupportedLanguage::kRu ||
+         supported_language == SupportedLanguage::kEs ||
+         supported_language == SupportedLanguage::kTr ||
+         supported_language == SupportedLanguage::kHi ||
+         supported_language == SupportedLanguage::kVi ||
+         supported_language == SupportedLanguage::kBn ||
+         supported_language == SupportedLanguage::kKn ||
+         supported_language == SupportedLanguage::kTa ||
+         supported_language == SupportedLanguage::kTe ||
+         supported_language == SupportedLanguage::kMr;
+}
+
 // Converts a SupportedLanguage to a language code.
 std::string_view ToLanguageCode(SupportedLanguage supported_language) {
   return kSupportedLanguageCodeMap.at(supported_language);
diff --git a/chrome/browser/on_device_translation/language_pack_util.h b/chrome/browser/on_device_translation/language_pack_util.h
index 8eef7ef..e6c17768 100644
--- a/chrome/browser/on_device_translation/language_pack_util.h
+++ b/chrome/browser/on_device_translation/language_pack_util.h
@@ -85,6 +85,10 @@
 std::optional<SupportedLanguage> ToSupportedLanguage(
     std::string_view language_code);
 
+// Returns whether the language is in the top 12 by number of native speakers.
+// https://en.wikipedia.org/wiki/List_of_languages_by_number_of_native_speakers#Top_languages_by_population
+bool IsPopularLanguage(SupportedLanguage supported_language);
+
 // The key for language pack components.
 enum class LanguagePackKey {
   kEn_Es = 0,
diff --git a/chrome/browser/on_device_translation/language_pack_util_unittest.cc b/chrome/browser/on_device_translation/language_pack_util_unittest.cc
index 43ab8c2..a1fc636d 100644
--- a/chrome/browser/on_device_translation/language_pack_util_unittest.cc
+++ b/chrome/browser/on_device_translation/language_pack_util_unittest.cc
@@ -106,6 +106,49 @@
   EXPECT_EQ(ToSupportedLanguage(""), std::nullopt);
 }
 
+TEST(LanguagePackUtilTest, IsPopularLanguage) {
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kEn));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kEn));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kEs));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kJa));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kAr));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kBn));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kDe));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kFr));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kHi));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kIt));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kKo));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kNl));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kPl));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kPt));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kRu));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kTh));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kTr));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kVi));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kZh));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kZhHant));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kBg));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kCs));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kDa));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kEl));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kFi));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kHr));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kHu));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kId));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kIw));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kLt));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kNo));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kRo));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kSk));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kSl));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kSv));
+  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kUk));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kKn));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kTa));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kTe));
+  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kMr));
+}
+
 TEST(LanguagePackUtilTest, GetLanguagePackComponentConfig) {
   // En to Es.
   EXPECT_EQ(GetLanguagePackComponentConfig(LanguagePackKey::kEn_Es).language1,
diff --git a/chrome/browser/on_device_translation/on_device_translation_browsertest.cc b/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
index 2a95d31..e07d1fe0 100644
--- a/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
+++ b/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
@@ -186,6 +186,8 @@
     case CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired:
       return "downloadable";
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
+    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
+    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
     case CanCreateTranslatorResult::kNoServiceCrashed:
     case CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case CanCreateTranslatorResult::kNoExceedsServiceCountLimitation:
@@ -360,36 +362,6 @@
     EXPECT_FALSE(console_observer.messages().empty());
   }
 
-  void ClearSiteContentSettings() {
-    content::BrowsingDataRemover* remover =
-        browser()->profile()->GetBrowsingDataRemover();
-    content::BrowsingDataRemoverCompletionObserver observer(remover);
-    remover->RemoveAndReply(
-        base::Time(), base::Time::Max(),
-        chrome_browsing_data_remover::DATA_TYPE_CONTENT_SETTINGS,
-        chrome_browsing_data_remover::ALL_ORIGIN_TYPES, &observer);
-    observer.BlockUntilCompletion();
-  }
-
-  content::RenderFrameHost* CreateIframe(Browser* target_browser = nullptr) {
-    EXPECT_EQ(EvalJsCatchingError(R"(
-      window._iframe = document.createElement('iframe');
-      document.body.appendChild(window._iframe);
-      return "OK";
-  )",
-                                  target_browser),
-              "OK");
-
-    return ChildFrameAt((target_browser ? target_browser : browser())
-                            ->tab_strip_model()
-                            ->GetActiveWebContents(),
-                        0);
-  }
-
-  bool RemoveIframe(Browser* target_browser = nullptr) {
-    return ExecJs("document.body.removeChild(window._iframe);");
-  }
-
  private:
   base::ScopedTempDir tmp_dir_;
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -502,13 +474,10 @@
             "en to ja: hello");
 }
 
-// TODO(crbug.com/421947718): Disabled because there's a race between triggering
-// user activation and consuming it when calling `create` multiple times.
-//
 // Tests the behavior of multiple create() calls with different
 // source/target languages.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       DISABLED_CreateTranslatorInstallMultipleLanguagePacks) {
+                       CreateTranslatorInstallMultipleLanguagePacks) {
   MockComponentManager mock_component_manager(GetTempDir());
   NavigateToEmptyPage();
 
@@ -533,45 +502,41 @@
         run_loop_for_register_en_es_language_pack.Quit();
       }));
 
-  // Helper function to get the state of a promise at the moment the helper
-  // function is called.
-  EXPECT_TRUE(ExecJs(R"(
-    self.getPromiseState = async promise => {
-        const symbol = Symbol();
-        try {
-          const result = await Promise.race([promise, Promise.resolve(symbol)]);
-          return result == symbol ? "pending" : "fulfilled";
-        } catch (e) {
-          return "rejected";
-        }
-    }
-  )"));
-
   // Create create() multiple times.
   //   1. En => Ja.
   //   2. En => Es.
   //   3. En => Ja.
-  EXPECT_TRUE(ExecJs(R"(
-      self.enJaPromise1 = Translator.create({
+  EXPECT_EQ(EvalJsCatchingError(R"(
+      window._testEnJaPromise1 = Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'ja',
         });
-  )",
-                     browser(), content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
-  EXPECT_TRUE(ExecJs(R"(
-      self.enEsPromise = Translator.create({
+      window._testEnJaPromise1Resolved = false;
+      window._testEnJaPromise1.then(() => {
+        window._testEnJaPromise1Resolved = true;
+      });
+
+      window._testEnEsPromise = Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'es',
         });
-  )",
-                     browser(), content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
-  EXPECT_TRUE(ExecJs(R"(
-      self.enJaPromise2 = Translator.create({
+      window._testEnEsPromiseResolved = false;
+      window._testEnEsPromise.then(() => {
+        window._testEnEsPromiseResolved = true;
+      });
+
+      window._testEnJaPromise2 = Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'ja',
         });
-  )",
-                     browser(), content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
+      window._testEnJaPromise2Resolved = false;
+      window._testEnJaPromise2.then(() => {
+        window._testEnJaPromise2Resolved = true;
+      });
+
+      return 'OK';
+  )"),
+            "OK");
 
   // Wait until RegisterTranslateKitComponentImpl() is called.
   run_loop_for_register_translate_kit.Run();
@@ -584,40 +549,40 @@
   mock_component_manager.InstallMockTranslateKitComponent();
 
   // All promises should not be resolved yet.
-  EXPECT_EQ(EvalJs("getPromiseState(enJaPromise1)").ExtractString(), "pending");
-  EXPECT_EQ(EvalJs("getPromiseState(enEsPromise)").ExtractString(), "pending");
-  EXPECT_EQ(EvalJs("getPromiseState(enJaPromise2)").ExtractString(), "pending");
+  EXPECT_FALSE(EvalJs("window._testEnJaPromise1Resolved").ExtractBool());
+  EXPECT_FALSE(EvalJs("window._testEnJaPromise2Resolved").ExtractBool());
+  EXPECT_FALSE(EvalJs("window._testEnEsPromiseResolved").ExtractBool());
 
   // Install the mock `en_ja` language pack.
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ja);
 
   // Translate to Japanese. Both `en_ja` promises should be resolved now.
-  EXPECT_EQ(EvalJsCatchingError(
-                "return await (await enJaPromise1).translate('hello');"),
-            "en to ja: hello");
   EXPECT_EQ(
-      EvalJsCatchingError("return await (await enJaPromise2).translate('hi');"),
+      EvalJsCatchingError(
+          "return await (await window._testEnJaPromise1).translate('hello');"),
+      "en to ja: hello");
+  EXPECT_EQ(
+      EvalJsCatchingError(
+          "return await (await window._testEnJaPromise2).translate('hi');"),
       "en to ja: hi");
 
   // The promise of `en_es` should not be resolved yet.
-  EXPECT_EQ(EvalJs("getPromiseState(enEsPromise)").ExtractString(), "pending");
+  EXPECT_FALSE(EvalJs("window._testEnEsPromiseResolved").ExtractBool());
 
   // Install the mock `en_es` language pack.
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Es);
 
   // Translate to Spanish. The `en_es` promise should be resolved now.
-  EXPECT_EQ(EvalJsCatchingError(
-                "return await (await self.enEsPromise).translate('hello');"),
-            "en to es: hello");
+  EXPECT_EQ(
+      EvalJsCatchingError(
+          "return await (await window._testEnEsPromise).translate('hello');"),
+      "en to es: hello");
 }
 
-// TODO(crbug.com/421947718): Disabled because there's a race between triggering
-// user activation and consuming it when calling `create` multiple times.
-//
 // Tests the behavior of create() when the number of pending tasks
 // exceeds the limit.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       DISABLED_ExceedMaxPendingTaskCount) {
+                       ExceedMaxPendingTaskCount) {
   MockComponentManager mock_component_manager(GetTempDir());
   NavigateToEmptyPage();
 
@@ -636,12 +601,6 @@
         run_loop_for_register_language_pack.Quit();
       }));
 
-  // TODO(crbug.com/421947718): Each `Translator.create` call should be in it's
-  // own `EvalJs` call like
-  // `CreateTranslator_Delay_ForTranslatorCreatedDuringInitialTranslatorCreationWithDelay`,
-  // but since we're blocked on the race issue from crbug.com/421947718, this
-  // hasn't been updated yet.
-  //
   // Call create() kMaxPendingTaskCount times.
   EXPECT_EQ(EvalJsCatchingError(base::StringPrintf(R"(
       window._testPromises = [];
@@ -711,6 +670,49 @@
             "OK");
 }
 
+// Tests the behavior of TranslationAPILimitLanguagePackCount
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
+                       ExceedLanguagePackCount) {
+  MockComponentManager mock_component_manager(GetTempDir());
+  mock_component_manager.ExpectCallRegisterTranslateKitComponentAndInstall();
+  const base::span<const LanguagePackKey> language_packs =
+      base::span(kLanguagePackKeys);
+  NavigateToEmptyPage();
+
+  // Get the amount of packages we can install and assert that we have enough
+  // language packs for this test.
+  size_t installable_package_count =
+      on_device_translation::GetInstallablePackageCount(0);
+  ASSERT_GE(language_packs.size(), installable_package_count + 1);
+
+  // Add all the languages we're going to test to the selected languages so we
+  // don't fail PassAcceptLanguagesCheck.
+  SetSelectedLanguages(language_packs.first(installable_package_count + 1));
+
+  // Test that we can install all the language packs up to the language pack
+  // limitation.
+  mock_component_manager.ExpectCallRegisterLanguagePackComponentAndInstall(
+      language_packs.first(installable_package_count));
+  for (const auto& language_pack_key :
+       language_packs.first(installable_package_count)) {
+    TestSimpleTranslationWorks(browser(), language_pack_key);
+  }
+
+  // The language pack count is equal to the limitation. So no more language
+  // pack can be downloaded.
+  auto console_observer = CreateConsoleObserver(
+      "The Translator API language pack count exceeded the limitation. See "
+      "https://developer.chrome.com/docs/ai/"
+      "translator-api?#supported-languages for more details.");
+
+  TestCreateTranslator(browser(), language_packs.at(installable_package_count),
+                       "NotSupportedError: Unable to create translator for the "
+                       "given source and target language.");
+
+  // The console message should be logged.
+  WaitForConsoleObserver(*console_observer);
+}
+
 // Tests the behavior of the failure of translation.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest, TranslationFailure) {
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1089,10 +1091,71 @@
   ExpectUpdatesAre(expected_updates);
 }
 
+// Tests V1 behavior.
+class OnDeviceTranslationV1BrowserTest : public OnDeviceTranslationBrowserTest {
+ public:
+  OnDeviceTranslationV1BrowserTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        blink::features::kTranslationAPIV1);
+  }
+  ~OnDeviceTranslationV1BrowserTest() override = default;
+
+ protected:
+  void ClearSiteContentSettings() {
+    content::BrowsingDataRemover* remover =
+        browser()->profile()->GetBrowsingDataRemover();
+    content::BrowsingDataRemoverCompletionObserver observer(remover);
+    remover->RemoveAndReply(
+        base::Time(), base::Time::Max(),
+        chrome_browsing_data_remover::DATA_TYPE_CONTENT_SETTINGS,
+        chrome_browsing_data_remover::ALL_ORIGIN_TYPES, &observer);
+    observer.BlockUntilCompletion();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// The language model limit is not triggered when the V1 flag is enabled.
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
+                       NoLanguageModelLimitation) {
+  MockComponentManager mock_component_manager(GetTempDir());
+  mock_component_manager.ExpectCallRegisterTranslateKitComponentAndInstall();
+  const base::span<const LanguagePackKey> language_packs =
+      base::span(kLanguagePackKeys);
+  NavigateToEmptyPage();
+
+  // Expect that the number of available language packs is less than the
+  // installable language pack size, given there is no limitation in place.
+  size_t installable_package_count =
+      on_device_translation::GetInstallablePackageCount(0);
+  ASSERT_LE(language_packs.size() + 1, installable_package_count);
+
+  // Add all the languages we're going to test to the selected languages so we
+  // don't fail `PassAcceptLanguagesCheck`.
+  SetSelectedLanguages(language_packs);
+
+  // Test that we can install all of the possible language packs for
+  // translation.
+  mock_component_manager.ExpectCallRegisterLanguagePackComponentAndInstall(
+      language_packs);
+  for (const auto& language_pack_key : language_packs) {
+    TestSimpleTranslationWorks(browser(), language_pack_key);
+  }
+
+  // Get the last language pack key.
+  LanguagePackKey last_language_pack = *(language_packs.end() - 1);
+
+  // Confirm that the last language pack install succeeded.
+  TestTranslationAvailable(browser(), GetSourceLanguageCode(last_language_pack),
+                           GetTargetLanguageCode(last_language_pack),
+                           "available");
+}
+
 // Confirms that `Translator.availability()` is not masked for a translation
 // containing only English or the user's preferred languages.
 IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationBrowserTest,
+    OnDeviceTranslationV1BrowserTest,
     TranslatorAvailabilityNotMasked_EnglishAndPreferredLanguages) {
   SetSelectedLanguages("fr");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1110,7 +1173,7 @@
 
 // Tests that `Translator.availability()` for a translation
 // containing a language outside of English + the user's preferred languages.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
                        TranslatorAvailabilityMasked_ForNonPreferredLanguages) {
   SetSelectedLanguages("fr");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1146,7 +1209,7 @@
 // A delay is triggered for a "downloadable" translation containing a language
 // outside of English + preferred languages.
 IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationBrowserTest,
+    OnDeviceTranslationV1BrowserTest,
     CreateTranslator_Delay_ForMaskedDownloadableTranslation) {
   // Setup Translate Kit Component and select Spanish as the preferred language.
   SetSelectedLanguages("en,es");
@@ -1171,15 +1234,12 @@
   TestSimpleTranslationWorks(browser(), "en", "ja");
 }
 
-// TODO(crbug.com/421947718): Disabled because there's a race between triggering
-// user activation and consuming it when calling `create` multiple times.
-//
 // A delay is triggered when a second translator for a given translation is
 // created during the delay time window of an initial translator's creation
 // (which is also expected to trigger a delay).
 IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationBrowserTest,
-    DISABLED_CreateTranslator_Delay_ForTranslatorCreatedDuringInitialTranslatorCreationWithDelay) {
+    OnDeviceTranslationV1BrowserTest,
+    CreateTranslator_Delay_ForTranslatorCreatedDuringInitialTranslatorCreationWithDelay) {
   SetSelectedLanguages("es");
   MockComponentManager mock_component_manager(GetTempDir());
   mock_component_manager.InstallMockTranslateKitComponent();
@@ -1218,7 +1278,7 @@
 // `Translator.create` should still require user activation if the language pair
 // is readily available but the site hasn't created a Translator for the
 // language pair yet.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
                        CreateRequiresUserActivationWhenDownloadedButMasked) {
   SetSelectedLanguages("es");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1241,7 +1301,7 @@
 
 // No delay is triggered for a "downloadable" translation between English +
 // preferred languages.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
                        CreateTranslator_NoDelay_DownloadableTranslation) {
   SetSelectedLanguages("en,es");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1262,7 +1322,7 @@
 
 // No delay is triggered in attempt to create a translator for an unsupported
 // language.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
                        CreateTranslator_NoDelay_UnsupportedLanguage) {
   SetSelectedLanguages("en,xx");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1421,17 +1481,16 @@
 
   NavigateToEmptyPage();
 
-  content::RenderFrameHost* iframe = CreateIframe();
-
   // Create a translator in an iframe.
-  EXPECT_EQ(content::EvalJs(iframe, R"(
-      Translator.create({
+  EXPECT_EQ(EvalJsCatchingError(R"(
+      window._testIframe = document.createElement('iframe');
+      document.body.appendChild(window._testIframe);
+      window._testIframe.contentWindow.Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'ja',
         });
-      'OK';
-    )")
-                .ExtractString(),
+      return 'OK';
+    )"),
             "OK");
   // Wait until RegisterTranslateKitComponentImpl() is called.
   run_loop_for_register_translate_kit.Run();
@@ -1439,7 +1498,7 @@
   run_loop_for_register_language_pack.Run();
 
   // Deletes the iframe after the browser process receives the request.
-  EXPECT_TRUE(RemoveIframe());
+  EXPECT_TRUE(ExecJs("document.body.removeChild(window._testIframe);"));
 
   // Install the mock TranslateKit component.
   mock_component_manager.InstallMockTranslateKitComponent();
@@ -1522,18 +1581,16 @@
   // Set the idle timeout to be 100 microseconds.
   service_controller->SetServiceIdleTimeoutForTesting(base::Microseconds(100));
 
-  content::RenderFrameHost* iframe = CreateIframe();
-
   // Test that Translator API on an iframe works.
-  EXPECT_EQ(content::EvalJs(iframe, R"(
-     (async () => {
-        const translator =
-            await Translator.create({
-              sourceLanguage: 'en',
-              targetLanguage: 'ja',
-            });
-        return await translator.translate('hello');
-      })();
+  EXPECT_EQ(EvalJsCatchingError(R"(
+      window._iframe = document.createElement('iframe');
+      document.body.appendChild(window._iframe);
+      const translator =
+          await window._iframe.contentWindow.Translator.create({
+            sourceLanguage: 'en',
+            targetLanguage: 'ja',
+          });
+      return await translator.translate('hello');
     )"),
             "en to ja: hello");
   // Check that the service is still running.
@@ -1567,15 +1624,7 @@
       .Times(0);
   mock_component_manager.InstallMockTranslateKitComponent();
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ja);
-
-  // Despite being ready, the availability will be masked since the site hasn't
-  // created a translator for this language pair yet.
-  // `kAfterDownloadTranslatorCreationRequired` is only ever returned in that
-  // situation, so receiving that value confirms that the package is readily
-  // available.
-  TestCanTranslateResult(
-      "en", "ja",
-      CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired);
+  TestCanTranslateResult("en", "ja", CanCreateTranslatorResult::kReadily);
 }
 
 // Test the behavior of availability() when the language pack is not ready.
@@ -1630,6 +1679,118 @@
                          CanCreateTranslatorResult::kNoNotSupportedLanguage);
 }
 
+// Test the behavior of availability() when the language pack is not ready, and
+// the language pack count will exceed the limitation.
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
+                       CanTranslateNoExceedsLanguagePackCountLimitation) {
+  // This test case uses English as the source language and French as the target
+  // language. To avoid the failure of PassAcceptLanguagesCheck(), we set the
+  // SelectedLanguages to be English and French.
+  SetSelectedLanguages("en,fr");
+  MockComponentManager mock_component_manager(GetTempDir());
+  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
+      .Times(0);
+  mock_component_manager.InstallMockTranslateKitComponent();
+
+  // No language packs are installed yet.
+  size_t installed_package_count = 0;
+
+  // Get the amount of packages we can install.
+  size_t installable_package_count =
+      on_device_translation::GetInstallablePackageCount(
+          installed_package_count);
+  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
+
+  for (const auto& language_pack_key : kLanguagePackKeys) {
+    mock_component_manager.InstallMockLanguagePack(language_pack_key);
+    installed_package_count++;
+
+    if (installed_package_count < installable_package_count) {
+      // The language pack count is less than the limitation.
+      TestCanTranslateResult(
+          "en", "fr",
+          CanCreateTranslatorResult::kAfterDownloadLanguagePackNotReady);
+    } else {
+      // The language pack count is equal to the limitation. So no more language
+      // pack can be downloaded.
+      TestCanTranslateResult(
+          "en", "fr",
+          CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation);
+      break;
+    }
+  }
+
+  ASSERT_EQ(installed_package_count, installable_package_count);
+}
+
+// Test the behavior of availability() when the language pack is not ready, and
+// the language pack count exceed the limitation after downloading two language
+// packs.
+IN_PROC_BROWSER_TEST_F(
+    OnDeviceTranslationBrowserTest,
+    CanTranslateNoExceedsLanguagePackCountLimitationTwoPackagesRequired) {
+  // This test case use Hindi and French as the source and target languages.
+  // To translate from Hindi to French, two language packs are required one for
+  // hi->en and one for en->fr.
+  SetSelectedLanguages("hi,fr");
+  MockComponentManager mock_component_manager(GetTempDir());
+  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
+      .Times(0);
+  mock_component_manager.InstallMockTranslateKitComponent();
+
+  // No language packs are installed yet.
+  size_t installed_package_count = 0;
+
+  // Get the amount of packages we can install.
+  size_t installable_package_count =
+      on_device_translation::GetInstallablePackageCount(
+          installed_package_count);
+  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
+
+  for (const auto& language_pack_key : kLanguagePackKeys) {
+    mock_component_manager.InstallMockLanguagePack(language_pack_key);
+    installed_package_count++;
+
+    if (installed_package_count < installable_package_count - 1) {
+      // The language pack count is less than the limitation.
+      TestCanTranslateResult(
+          "hi", "fr",
+          CanCreateTranslatorResult::kAfterDownloadLanguagePackNotReady);
+    } else if (installed_package_count < installable_package_count) {
+      // The language pack count is less than the limitation. But if
+      // we download the required language packs, the language pack count will
+      // exceed the limitation. So availability() returns `no`.
+      TestCanTranslateResult(
+          "hi", "fr",
+          CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation);
+    } else {
+      // The language pack count is 3, which is equal to the limitation. So no
+      // more language pack can be downloaded.
+      TestCanTranslateResult(
+          "hi", "fr",
+          CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation);
+      break;
+    }
+  }
+
+  ASSERT_EQ(installed_package_count, installable_package_count);
+}
+
+// Test the behavior of availability() when PassAcceptLanguagesCheck() checks
+// fails.
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
+                       CanTranslateNoAcceptLanguagesCheckFailed) {
+  MockComponentManager mock_component_manager(GetTempDir());
+  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
+      .Times(0);
+  mock_component_manager.InstallMockTranslateKitComponent();
+  mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ko);
+  // Korean is not treated as a popular language. So if `ko` is not in the
+  // accept languages, PassAcceptLanguagesCheck() will return false.
+  TestCanTranslateResult(
+      "en", "ko", CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed);
+}
+
 // Test the behavior of `availability()` when the execution context is not
 // valid.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
@@ -1667,6 +1828,20 @@
   TestTranslationAvailable(browser(), "en", "xx", "unavailable");
 }
 
+// Test the behavior of `availability()` when the `PassAcceptLanguagesCheck()`
+// check fails.
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
+                       Availability_Unavailable_AcceptLanguagesCheckFailed) {
+  MockComponentManager mock_component_manager(GetTempDir());
+  mock_component_manager.InstallMockTranslateKitComponent();
+  mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ko);
+  NavigateToEmptyPage();
+
+  // Korean is not treated as a popular language. So if `ko` is not in the
+  // accept languages, `PassAcceptLanguagesCheck()` will return false.
+  TestTranslationAvailable(browser(), "en", "ko", "unavailable");
+}
+
 // Test the behavior of `availability()` where the source language and the
 // target language are the same language.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
@@ -1679,6 +1854,92 @@
   TestTranslationAvailable(browser(), "en", "en", "unavailable");
 }
 
+// Test the behavior of `availability()` when the language pack is not ready,
+// and the language pack count will exceed the imitation.
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
+                       Availability_No_ExceedsLangPackCountLimitation) {
+  // This test case uses English as the source language and French as the target
+  // language. To avoid the failure of `PassAcceptLanguagesCheck()`, we set the
+  // preferred languages to English and French.
+  SetSelectedLanguages("en,fr");
+  MockComponentManager mock_component_manager(GetTempDir());
+  NavigateToEmptyPage();
+  mock_component_manager.InstallMockTranslateKitComponent();
+
+  // No language packs are installed yet.
+  size_t installed_package_count = 0;
+
+  // Get the amount of packages we can install.
+  size_t installable_package_count =
+      on_device_translation::GetInstallablePackageCount(
+          installed_package_count);
+  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
+
+  for (const auto& language_pack_key : kLanguagePackKeys) {
+    mock_component_manager.InstallMockLanguagePack(language_pack_key);
+    installed_package_count++;
+
+    if (installed_package_count < installable_package_count) {
+      // The language pack count is less than the limitation.
+      TestTranslationAvailable(browser(), "en", "fr", "downloadable");
+    } else {
+      // The language pack count is equal to the limitation. So no more language
+      // pack can be downloaded.
+      TestTranslationAvailable(browser(), "en", "fr", "unavailable");
+      break;
+    }
+  }
+
+  ASSERT_EQ(installed_package_count, installable_package_count);
+}
+
+// Test the behavior of `availability()` when the language pack is not ready,
+// and the language pack count exceeds the limitation after downloading two
+// more language packs.
+IN_PROC_BROWSER_TEST_F(
+    OnDeviceTranslationBrowserTest,
+    Availability_Unavailable_ExceedsLangPackCountLimitationTwoPackagesRequired) {
+  // This test case uses Hindi and French as the source and target languages.
+  // To translate from Hindi to French, two language packs are required: one for
+  // the hi->en translation and one for the en->fr translation.
+  SetSelectedLanguages("hi,fr");
+  MockComponentManager mock_component_manager(GetTempDir());
+  NavigateToEmptyPage();
+  mock_component_manager.InstallMockTranslateKitComponent();
+
+  // No language packs are installed yet.
+  size_t installed_package_count = 0;
+
+  // Get the amount of packages we can install.
+  size_t installable_package_count =
+      on_device_translation::GetInstallablePackageCount(
+          installed_package_count);
+  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
+
+  for (const auto& language_pack_key : kLanguagePackKeys) {
+    mock_component_manager.InstallMockLanguagePack(language_pack_key);
+    installed_package_count++;
+
+    if (installed_package_count < installable_package_count - 1) {
+      // The language pack count is less than the limitation.
+      TestTranslationAvailable(browser(), "hi", "fr", "downloadable");
+    } else if (installed_package_count < installable_package_count) {
+      // The language pack count is less than the limitation.
+      // If we download the required language packs, the language pack count
+      // will exceed the limitation. As a result, `availability()` is
+      // 'unavailable'.
+      TestTranslationAvailable(browser(), "hi", "fr", "unavailable");
+    } else {
+      // The language pack count is 3, which is equal to the limitation. As a
+      // result, no more language packs can be downloaded.
+      TestTranslationAvailable(browser(), "hi", "fr", "unavailable");
+      break;
+    }
+  }
+
+  ASSERT_EQ(installed_package_count, installable_package_count);
+}
+
 // Test the behavior of `availability()` when both the library and the language
 // packs are not ready.
 IN_PROC_BROWSER_TEST_F(
@@ -1824,14 +2085,7 @@
       .Times(0);
   mock_component_manager.InstallMockTranslateKitComponent();
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ko);
-  // Despite being ready, the availability will be masked since the site hasn't
-  // created a translator for this language pair yet.
-  // `kAfterDownloadTranslatorCreationRequired` is only ever returned in that
-  // situation, so receiving that value confirms that the package is readily
-  // available.
-  TestCanTranslateResult(
-      "en", "ko",
-      CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired);
+  TestCanTranslateResult("en", "ko", CanCreateTranslatorResult::kReadily);
 }
 
 // Test the behavior of Translator API in a cross origin iframe.
@@ -1873,19 +2127,14 @@
   }
 
   // Adds an iframe to the test page and optionally sets its permission policy.
-  content::RenderFrameHost* AddIframe(size_t index,
-                                      Browser* target_browser,
-                                      bool permission_policy_enabled) {
+  void AddIframe(size_t index,
+                 Browser* target_browser,
+                 bool permission_policy_enabled) {
     EXPECT_EQ(EvalJsCatchingError(JsReplace("return addIframe($1, $2);",
                                             CreateCrossOriginIframeUrl(index),
                                             permission_policy_enabled),
                                   target_browser),
               "loaded");
-
-    return ChildFrameAt((target_browser ? target_browser : browser())
-                            ->tab_strip_model()
-                            ->GetActiveWebContents(),
-                        index);
   }
 
   // Removes the iframe and waits for the service deletion.
@@ -1904,7 +2153,7 @@
 
   // Creates a translator and translates in the iframe. Returns successful
   // translation or the error message.
-  std::string CheckTranslateInIframe(content::RenderFrameHost* iframe) {
+  std::string CheckTranslateInIframe(size_t index, Browser* target_browser) {
     const std::string_view translateTestScript = R"(
         (async () => {
           try {
@@ -1920,11 +2169,14 @@
           }
         })()
       )";
-    return content::EvalJs(iframe, translateTestScript).ExtractString();
+    return EvalJsCatchingError(
+        JsReplace("return evalInIframe($1, $2);",
+                  CreateCrossOriginIframeUrl(index), translateTestScript),
+        target_browser);
   }
 
   // Checks the result of availability() in the iframe.
-  std::string TryCanTranslateInIframe(content::RenderFrameHost* iframe) {
+  std::string TryCanTranslateInIframe(size_t index, Browser* target_browser) {
     const std::string_view translateTestScript = R"(
       (async () => {
         try {
@@ -1937,7 +2189,10 @@
         }
       })()
     )";
-    return content::EvalJs(iframe, translateTestScript).ExtractString();
+    return EvalJsCatchingError(
+        JsReplace("return evalInIframe($1, $2);",
+                  CreateCrossOriginIframeUrl(index), translateTestScript),
+        target_browser);
   }
 
  private:
@@ -2013,12 +2268,11 @@
       .Times(0);
 
   NavigateToTestPage(browser());
-  content::RenderFrameHost* iframe =
-      AddIframe(0, browser(), /*enable_permission_policy=*/false);
+  AddIframe(0, browser(), /*enable_permission_policy=*/false);
 
   // Translation is not available in cross-origin iframes without permission
   // policy.
-  EXPECT_EQ(CheckTranslateInIframe(iframe), "NotAllowedError");
+  EXPECT_EQ(CheckTranslateInIframe(0, browser()), "NotAllowedError");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe when the
@@ -2035,27 +2289,25 @@
   // Until the service count exceeds the limit, the translator can be created,
   // and the translation is successful.
   for (; i < kTranslationAPIMaxServiceCount.Get(); i++) {
-    content::RenderFrameHost* iframe =
-        AddIframe(i, browser(), /*enable_permission_policy=*/true);
-    EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
-    EXPECT_EQ(TryCanTranslateInIframe(iframe), "available");
+    AddIframe(i, browser(), /*enable_permission_policy=*/true);
+    EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
+    EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
   }
 
   // When the service count exceeds the limit, the translator cannot be created,
   // even when the permission policy is still enabled.
-  content::RenderFrameHost* iframe =
-      AddIframe(i, browser(), /*enable_permission_policy=*/true);
+  AddIframe(i, browser(), /*enable_permission_policy=*/true);
   auto console_observer = CreateConsoleObserver(
       "The translation service count exceeded the limitation.");
-  EXPECT_EQ(CheckTranslateInIframe(iframe), "NotSupportedError");
+  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "NotSupportedError");
   WaitForConsoleObserver(*console_observer);
-  EXPECT_EQ(TryCanTranslateInIframe(iframe), "unavailable");
+  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "unavailable");
 
   // When the service count is back to under the limit, the translator can be
   // created again.
   RemoveIframeAndWaitForServiceDeletion(0, browser());
-  EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
-  EXPECT_EQ(TryCanTranslateInIframe(iframe), "available");
+  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
+  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe using the
@@ -2070,13 +2322,11 @@
   Browser* incognito_browser = CreateIncognitoBrowser();
 
   NavigateToTestPage(incognito_browser);
-  content::RenderFrameHost* iframe0 =
-      AddIframe(0, incognito_browser, /*enable_permission_policy=*/true);
-  EXPECT_EQ(CheckTranslateInIframe(iframe0), "en to ja: hello");
+  AddIframe(0, incognito_browser, /*enable_permission_policy=*/true);
+  EXPECT_EQ(CheckTranslateInIframe(0, incognito_browser), "en to ja: hello");
 
-  content::RenderFrameHost* iframe1 =
-      AddIframe(1, incognito_browser, /*enable_permission_policy=*/false);
-  EXPECT_EQ(CheckTranslateInIframe(iframe1), "NotAllowedError");
+  AddIframe(1, incognito_browser, /*enable_permission_policy=*/false);
+  EXPECT_EQ(CheckTranslateInIframe(1, incognito_browser), "NotAllowedError");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe using the
@@ -2091,9 +2341,8 @@
   Browser* guest_browser = CreateGuestBrowser();
 
   NavigateToTestPage(guest_browser);
-  content::RenderFrameHost* iframe =
-      AddIframe(0, guest_browser, /*enable_permission_policy=*/true);
-  EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
+  AddIframe(0, guest_browser, /*enable_permission_policy=*/true);
+  EXPECT_EQ(CheckTranslateInIframe(0, guest_browser), "en to ja: hello");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe using
@@ -2128,39 +2377,32 @@
   // translation is successful.
   for (size_t i = 0; i < kTranslationAPIMaxServiceCount.Get(); i++) {
     for (auto* target_browser : browsers) {
-      content::RenderFrameHost* iframe =
-          AddIframe(i, target_browser, /*enable_permission_policy=*/true);
-      EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
+      AddIframe(i, target_browser, /*enable_permission_policy=*/true);
+      EXPECT_EQ(CheckTranslateInIframe(i, target_browser), "en to ja: hello");
     }
   }
 
   const size_t limit_count = kTranslationAPIMaxServiceCount.Get();
 
-  std::vector<content::RenderFrameHost*> iframes;
-
   // When the service count per profile exceeds the limit, the translator
   // cannot be created.
   for (auto* target_browser : browsers) {
-    content::RenderFrameHost* iframe = AddIframe(
-        limit_count, target_browser, /*enable_permission_policy=*/true);
-    iframes.push_back(iframe);
+    AddIframe(limit_count, target_browser, /*enable_permission_policy=*/true);
     auto console_observer = CreateConsoleObserver(
         "The translation service count exceeded the limitation.",
         target_browser);
-    EXPECT_EQ(CheckTranslateInIframe(iframe), "NotSupportedError");
+    EXPECT_EQ(CheckTranslateInIframe(limit_count, target_browser),
+              "NotSupportedError");
     // The console message should be logged.
     WaitForConsoleObserver(*console_observer);
   }
 
-  ASSERT_EQ(iframes.size(), browsers.size());
-
   // When the service count per profile is back to under the limit, the
   // translator can be created again.
-  for (size_t i = 0; i < browsers.size(); i++) {
-    Browser* target_browser = browsers[i];
-    content::RenderFrameHost* iframe = iframes[i];
+  for (auto* target_browser : browsers) {
     RemoveIframeAndWaitForServiceDeletion(0, target_browser);
-    EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
+    EXPECT_EQ(CheckTranslateInIframe(limit_count, target_browser),
+              "en to ja: hello");
   }
 }
 
@@ -2195,23 +2437,21 @@
   // Until the service count exceeds the limit, the translator can be created,
   // and the translation is successful.
   for (; i < kTranslationAPIMaxServiceCount.Get(); i++) {
-    content::RenderFrameHost* iframe =
-        AddIframe(i, browser(), /*enable_permission_policy=*/true);
-    EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
-    EXPECT_EQ(TryCanTranslateInIframe(iframe), "available");
+    AddIframe(i, browser(), /*enable_permission_policy=*/true);
+    EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
+    EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
   }
 
   // When the service count exceeds the limit, the translator cannot be created.
-  content::RenderFrameHost* last_iframe =
-      AddIframe(i, browser(), /*enable_permission_policy=*/true);
-  EXPECT_EQ(CheckTranslateInIframe(last_iframe), "NotSupportedError");
-  EXPECT_EQ(TryCanTranslateInIframe(last_iframe), "unavailable");
+  AddIframe(i, browser(), /*enable_permission_policy=*/true);
+  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "NotSupportedError");
+  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "unavailable");
 
   // When the service count is back to under the limit, the translator can be
   // created again.
   RemoveIframeAndWaitForServiceDeletion(0, browser());
-  EXPECT_EQ(CheckTranslateInIframe(last_iframe), "en to ja: hello");
-  EXPECT_EQ(TryCanTranslateInIframe(last_iframe), "available");
+  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
+  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
 }
 
 // Tests the behavior of the Origin Trial token for the Translation API.
@@ -2447,15 +2687,7 @@
   mock_component_manager.InstallMockTranslateKitComponent();
   mock_component_manager.DoNotExpectCallRegisterLanguagePackComponent();
   NavigateToEmptyPage();
-
-  // Despite being ready, the availability will be masked since the site hasn't
-  // created a translator for this language pair yet.
-  // `kAfterDownloadTranslatorCreationRequired` is only ever returned in that
-  // situation, so receiving that value confirms that the package is readily
-  // available.
-  TestCanTranslateResult(
-      "en", "ja",
-      CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired);
+  TestCanTranslateResult("en", "ja", CanCreateTranslatorResult::kReadily);
 }
 
 // Tests the behavior of availability() when the required language package
diff --git a/chrome/browser/on_device_translation/service_controller.cc b/chrome/browser/on_device_translation/service_controller.cc
index bc48543..5ffb9c0 100644
--- a/chrome/browser/on_device_translation/service_controller.cc
+++ b/chrome/browser/on_device_translation/service_controller.cc
@@ -156,6 +156,19 @@
                                       to_be_registered_packs);
 
     if (!to_be_registered_packs.empty()) {
+      if (!base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
+          (kTranslationAPILimitLanguagePackCount.Get() &&
+           to_be_registered_packs.size() >
+               GetInstallablePackageCount(
+                   ComponentManager::GetRegisteredLanguagePacks().size()))) {
+        RecordLanguagePairUma(
+            "Translate.OnDeviceTranslation.DownloadExceedLimit.LanguagePair",
+            source_lang, target_lang);
+        std::move(callback).Run(base::unexpected(
+            CreateTranslatorError::kExceedsLanguagePackCountLimitation));
+        return;
+      }
+
       for (const auto& language_pack : to_be_registered_packs) {
         RecordLanguagePairUma(
             "Translate.OnDeviceTranslation.Download.LanguagePair",
@@ -307,6 +320,17 @@
     return CanCreateTranslatorResult::kNoNotSupportedLanguage;
   }
 
+  if (!to_be_registered_packs.empty() &&
+      !base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
+      kTranslationAPILimitLanguagePackCount.Get() &&
+      to_be_registered_packs.size() >
+          GetInstallablePackageCount(
+              ComponentManager::GetRegisteredLanguagePacks().size())) {
+    // The number of installed language packs will exceed the limitation if the
+    // new required language packs are installed.
+    return CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation;
+  }
+
   if (required_not_installed_packs.empty()) {
     // All required language packages are installed.
     if (ComponentManager::GetTranslateKitLibraryPath().empty()) {
diff --git a/chrome/browser/on_device_translation/translation_manager_impl.cc b/chrome/browser/on_device_translation/translation_manager_impl.cc
index e847883..1c07542 100644
--- a/chrome/browser/on_device_translation/translation_manager_impl.cc
+++ b/chrome/browser/on_device_translation/translation_manager_impl.cc
@@ -293,6 +293,15 @@
     return;
   }
 
+  if (!PassAcceptLanguagesCheck(GetAcceptLanguages(browser_context()),
+                                source_language, target_language)) {
+    mojo::Remote(std::move(client))
+        ->OnResult(CreateTranslatorResult::NewError(
+                       CreateTranslatorError::kAcceptLanguagesCheckFailed),
+                   nullptr, nullptr);
+    return;
+  }
+
   if (options->observer_remote) {
     base::flat_set<std::string> component_ids = {
         component_updater::TranslateKitComponentInstallerPolicy::
@@ -316,7 +325,8 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(client),
                      source_language, target_language);
 
-  if (add_fake_download_delay) {
+  if (base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
+      add_fake_download_delay) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
         FROM_HERE, std::move(create_translator), GetTranslatorDownloadDelay());
   } else {
@@ -364,6 +374,14 @@
   const std::vector<std::string_view> accept_languages =
       GetAcceptLanguages(browser_context());
 
+  // TODO(crbug.com/385173766): Remove once V1 is launched.
+  if (!PassAcceptLanguagesCheck(accept_languages, source_language,
+                                target_language)) {
+    std::move(callback).Run(
+        CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed);
+    return;
+  }
+
   bool are_source_and_target_accept_or_english =
       (IsInAcceptLanguage(accept_languages, source_language) ||
        l10n_util::GetLanguage(source_language) == "en") &&
@@ -371,6 +389,7 @@
        l10n_util::GetLanguage(target_language) == "en");
 
   bool mask_readily_result =
+      base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
       !HasInitializedTranslator(source_language, target_language) &&
       !are_source_and_target_accept_or_english;
 
diff --git a/chrome/browser/on_device_translation/translation_manager_util.cc b/chrome/browser/on_device_translation/translation_manager_util.cc
index aa6018d..b5c6b6d 100644
--- a/chrome/browser/on_device_translation/translation_manager_util.cc
+++ b/chrome/browser/on_device_translation/translation_manager_util.cc
@@ -18,6 +18,19 @@
 
 namespace on_device_translation {
 
+namespace {
+
+bool IsSupportedPopularLanguage(const std::string& lang) {
+  const std::optional<SupportedLanguage> supported_lang =
+      ToSupportedLanguage(lang);
+  if (!supported_lang) {
+    return false;
+  }
+  return IsPopularLanguage(*supported_lang);
+}
+
+}  // namespace
+
 const std::vector<std::string_view> GetAcceptLanguages(
     content::BrowserContext* browser_context) {
   CHECK(browser_context);
@@ -48,4 +61,34 @@
       ->GetBoolean(prefs::kTranslatorAPIAllowed);
 }
 
+bool PassAcceptLanguagesCheck(
+    const std::vector<std::string_view>& accept_languages,
+    const std::string& source_lang,
+    const std::string& target_lang) {
+  if (base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) ||
+      !kTranslationAPIAcceptLanguagesCheck.Get()) {
+    return true;
+  }
+
+  // TODO(crbug.com/371899260): Implement better language code handling.
+
+  // One of the source or the destination language must be in the user's accept
+  // language.
+  const bool source_lang_is_in_accept_langs =
+      IsInAcceptLanguage(accept_languages, source_lang);
+  const bool target_lang_is_in_accept_langs =
+      IsInAcceptLanguage(accept_languages, target_lang);
+
+  // The other language must be a popular language.
+  if (!source_lang_is_in_accept_langs &&
+      !IsSupportedPopularLanguage(source_lang)) {
+    return false;
+  }
+  if (!target_lang_is_in_accept_langs &&
+      !IsSupportedPopularLanguage(target_lang)) {
+    return false;
+  }
+  return true;
+}
+
 }  // namespace on_device_translation
diff --git a/chrome/browser/on_device_translation/translation_manager_util.h b/chrome/browser/on_device_translation/translation_manager_util.h
index 09910fb..67b24d2 100644
--- a/chrome/browser/on_device_translation/translation_manager_util.h
+++ b/chrome/browser/on_device_translation/translation_manager_util.h
@@ -27,6 +27,14 @@
 // Determines if the Translator API is enabled.
 bool IsTranslatorAllowed(content::BrowserContext* browser_context);
 
+// When the `TranslationAPIAcceptLanguagesCheck` feature is enabled, the
+// Translation API will fail if neither the source nor destination language is
+// in Accept Languages. This is intended to mitigate privacy concerns.
+bool PassAcceptLanguagesCheck(
+    const std::vector<std::string_view>& accept_languages,
+    const std::string& source_lang,
+    const std::string& target_lang);
+
 // Implementation of LookupMatchingLocaleByBestFit
 // (https://tc39.es/ecma402/#sec-lookupmatchinglocalebybestfit) as
 // LookupMatchingLocaleByPrefix
diff --git a/chrome/browser/on_device_translation/translation_manager_util_unittest.cc b/chrome/browser/on_device_translation/translation_manager_util_unittest.cc
index a5c767b..4033fc7 100644
--- a/chrome/browser/on_device_translation/translation_manager_util_unittest.cc
+++ b/chrome/browser/on_device_translation/translation_manager_util_unittest.cc
@@ -19,6 +19,143 @@
   ~TranslationManagerUtilTest() override = default;
 };
 
+TEST_F(TranslationManagerUtilTest, PassAcceptLanguagesCheck) {
+  // Source lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : true
+  // Target lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : true
+  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "en", "es"));
+
+  // Source lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : true
+  // Target lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : false
+  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "fr"}, "en", "fr"));
+
+  // Source lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : true
+  // Target lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : true
+  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "en", "zh"));
+
+  // Source lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : true
+  // Target lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : false
+  // Target is not in accept-languages, and not popular.
+  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "en", "fr"));
+
+  // Source lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : false
+  // Target lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : true
+  EXPECT_TRUE(PassAcceptLanguagesCheck({"de", "es"}, "de", "es"));
+
+  // Source lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : false
+  // Target lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : false
+  EXPECT_TRUE(PassAcceptLanguagesCheck({"de", "fr"}, "de", "fr"));
+
+  // Source lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : false
+  // Target lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : true
+  EXPECT_TRUE(PassAcceptLanguagesCheck({"de", "es"}, "de", "zh"));
+
+  // Source lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : false
+  // Target lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : false
+  // Target is not in accept-languages, and not popular.
+  EXPECT_FALSE(PassAcceptLanguagesCheck({"de", "es"}, "de", "fr"));
+
+  // Source lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : true
+  // Target lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : true
+  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "ja", "es"));
+
+  // Source lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : true
+  // Target lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : false
+  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "fr"}, "ja", "fr"));
+
+  // Source lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : true
+  // Target lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : true
+  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "ja", "zh"));
+
+  // Source lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : true
+  // Target lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : false
+  // Target is not in accept-languages, and not popular.
+  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "ja", "fr"));
+
+  // Source lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : false
+  // Target lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : true
+  // Source is not in accept-languages, and not popular.
+  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "de", "es"));
+
+  // Source lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : false
+  // Target lang:
+  //   - Is in accept-languages : true
+  //   - Is popular lang        : false
+  // Source is not in accept-languages, and not popular.
+  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "fr"}, "de", "fr"));
+
+  // Source lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : false
+  // Target lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : true
+  // Source is not in accept-languages, and not popular.
+  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "de", "zh"));
+
+  // Source lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : false
+  // Target lang:
+  //   - Is in accept-languages : false
+  //   - Is popular lang        : false
+  // Target and source are not in accept-languages, and not popular.
+  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "de", "fr"));
+}
+
 TEST_F(TranslationManagerUtilTest,
        LookupMatchingLocaleByBestFitFindsSupportedLanguageTag) {
   std::vector<std::string> en_variations{
diff --git a/chrome/browser/page_load_metrics/observers/gws_hp_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/gws_hp_page_load_metrics_observer.cc
index 55a488e..5489039 100644
--- a/chrome/browser/page_load_metrics/observers/gws_hp_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/gws_hp_page_load_metrics_observer.cc
@@ -10,8 +10,8 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/named_trigger.h"
+#include "base/trace_event/trace_event.h"
 #include "chrome/browser/after_startup_task_utils.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/page_load_metrics/observers/histogram_suffixes.h"
diff --git a/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc
index a21f29a..6269227e 100644
--- a/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc
@@ -8,7 +8,7 @@
 
 #include "base/check_is_test.h"
 #include "base/notreached.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.h"
 #include "chrome/browser/predictors/loading_predictor.h"
 #include "chrome/browser/predictors/loading_predictor_factory.h"
diff --git a/chrome/browser/performance_manager/metrics/metrics_provider_desktop.cc b/chrome/browser/performance_manager/metrics/metrics_provider_desktop.cc
index 5c128365..517dd7c 100644
--- a/chrome/browser/performance_manager/metrics/metrics_provider_desktop.cc
+++ b/chrome/browser/performance_manager/metrics/metrics_provider_desktop.cc
@@ -13,7 +13,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "base/timer/timer.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
diff --git a/chrome/browser/performance_manager/policies/keep_alive_dse_policy_unittest.cc b/chrome/browser/performance_manager/policies/keep_alive_dse_policy_unittest.cc
index 6708c5f..37c359a 100644
--- a/chrome/browser/performance_manager/policies/keep_alive_dse_policy_unittest.cc
+++ b/chrome/browser/performance_manager/policies/keep_alive_dse_policy_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/performance_manager/policies/keep_alive_dse_policy.h"
 
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/performance_manager/public/performance_manager.h"
diff --git a/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc
index 3958a4c..882dea4 100644
--- a/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc
@@ -605,18 +605,15 @@
   LoadTabAndEnterPictureInPicture(browser(),
                                   maximum_window_size + gfx::Size(1000, 2000));
 
-  // Confirm that the size of the outer window bounds are equal to or less than
-  // the maximum size.
+  // Confirm that the size of the outer window bounds are equal to the maximum
+  // window size.
   auto* pip_web_contents = window_controller()->GetChildWebContents();
   ASSERT_NE(nullptr, pip_web_contents);
   WaitForPageLoad(pip_web_contents);
 
   auto* browser_view = static_cast<BrowserView*>(
       BrowserWindow::FindBrowserWindowWithWebContents(pip_web_contents));
-  EXPECT_LE(browser_view->GetBounds().size().width(),
-            maximum_window_size.width());
-  EXPECT_LE(browser_view->GetBounds().size().height(),
-            maximum_window_size.height());
+  ASSERT_EQ(maximum_window_size, browser_view->GetBounds().size());
 }
 
 // Context menu should not be shown when right clicking on a document picture in
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
index 395f622..ebdc395e 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
@@ -56,11 +56,6 @@
 constexpr double kMaxWindowSizeRatio = 0.8;
 
 #if !BUILDFLAG(IS_ANDROID)
-// The largest fraction of the screen that Document Picture-in-Picture windows
-// can take up by request of the website. The user can still manually resize to
-// `kMaxWindowSizeRatio`.
-constexpr double kMaxSiteRequestedWindowSizeRatio = 0.25;
-
 // Returns true if a document picture-in-picture window should be focused upon
 // opening it.
 bool ShouldFocusPictureInPictureWindow(const NavigateParams& params) {
@@ -77,13 +72,6 @@
   // AutoPictureInPictureTabHelper.
   return !auto_picture_in_picture_tab_helper->IsInAutoPictureInPicture();
 }
-
-// Returns the maximum area in pixels that the site can request a
-// picture-in-picture window to be.
-base::CheckedNumeric<int> GetMaximumSiteRequestedWindowArea(
-    const display::Display& display) {
-  return display.size().GetCheckedArea() * kMaxSiteRequestedWindowSizeRatio;
-}
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace
@@ -298,107 +286,6 @@
   return instance->GetChildWebContents() == wc;
 }
 
-// static
-gfx::Size PictureInPictureWindowManager::AdjustRequestedSizeIfNecessary(
-    const gfx::Size& requested_size,
-    const display::Display& display) {
-#if BUILDFLAG(IS_ANDROID)
-  return requested_size;
-#else   // BUILDFLAG(IS_ANDROID)
-  base::CheckedNumeric<int> requested_area = requested_size.GetCheckedArea();
-  base::CheckedNumeric<int> max_requested_area =
-      GetMaximumSiteRequestedWindowArea(display);
-
-  // If the website has requested an area too large to calculate, then their
-  // request isn't particularly useful and we will fall back to the minimum
-  // size.
-  if (!requested_area.IsValid()) {
-    return GetMinimumInnerWindowSize();
-  }
-
-  // If the screen size is too large to calculate, then fall back to allowing
-  // the requested size. Note that this should only occur with a ridiculous
-  // monitor size that would only happen in a test environment.
-  if (!max_requested_area.IsValid()) {
-    return requested_size;
-  }
-
-  // If the website's requested size is not too large, then there's nothing that
-  // needs to change.
-  if (requested_area.ValueOrDie() <= max_requested_area.ValueOrDie()) {
-    return requested_size;
-  }
-
-  // Otherwise, if the website's requested size is too large, then shrink it to
-  // the maximum allowed size while maintaining the given aspect ratio.
-  gfx::Size minimum_size(GetMinimumInnerWindowSize());
-  gfx::Size maximum_size(GetMaximumWindowSize(display));
-  maximum_size.SetToMax(minimum_size);
-
-  double original_width = static_cast<double>(requested_size.width());
-  double original_height = static_cast<double>(requested_size.height());
-
-  // Ideally, we could resize to perfectly maintain the aspect ratio while
-  // hitting the max requested area.
-  double ideal_scale_for_area =
-      std::sqrt(static_cast<double>(max_requested_area.ValueOrDie()) /
-                static_cast<double>(requested_area.ValueOrDie()));
-
-  // However, we need to ensure that we remain large enough for the minimum size
-  // in both dimensions.
-  double scale_needed_for_min_width =
-      static_cast<double>(minimum_size.width()) / original_width;
-  double scale_needed_for_min_height =
-      static_cast<double>(minimum_size.height()) / original_height;
-  double minimum_scale =
-      std::max(scale_needed_for_min_width, scale_needed_for_min_height);
-
-  // And also that we remain small enough to be within the maximum size in both
-  // dimensions.
-  double scale_needed_for_max_width =
-      static_cast<double>(maximum_size.width()) / original_width;
-  double scale_needed_for_max_height =
-      static_cast<double>(maximum_size.height()) / original_height;
-  double maximum_scale =
-      std::min(scale_needed_for_max_width, scale_needed_for_max_height);
-
-  gfx::Size output_size;
-
-  // If the smallest scale needed to reach the minimum size is larger than the
-  // largest scale that fits within the maximum bounds, then we can't perfectly
-  // maintain aspect ratio.
-  if (minimum_scale > maximum_scale) {
-    if (original_width > original_height) {
-      // If this is because the requested width is too large, then fall back to
-      // the minimum height with as much width as is allowed.
-      output_size.set_width(
-          static_cast<double>(max_requested_area.ValueOrDie()) /
-          minimum_size.height());
-      output_size.set_height(minimum_size.height());
-    } else {
-      // If this is because the requested height is too large, then fall back to
-      // the minimum width with as much height as is allowed.
-      output_size.set_width(minimum_size.width());
-      output_size.set_height(
-          static_cast<double>(max_requested_area.ValueOrDie()) /
-          minimum_size.width());
-    }
-  } else {
-    // Otherwise, either scale by the ideal factor or make it smaller than that
-    // to fit within the maximum size.
-    double effective_scale = std::min(ideal_scale_for_area, maximum_scale);
-    output_size.set_width(original_width * effective_scale);
-    output_size.set_height(original_height * effective_scale);
-  }
-
-  // Ensure the standard size restrictions are still met.
-  output_size.SetToMax(minimum_size);
-  output_size.SetToMin(maximum_size);
-
-  return output_size;
-#endif  // BUILDFLAG(IS_ANDROID)
-}
-
 std::optional<gfx::Rect>
 PictureInPictureWindowManager::GetPictureInPictureWindowBounds() const {
   return pip_window_controller_ ? pip_window_controller_->GetWindowBounds()
@@ -441,27 +328,14 @@
   }
 
   if (pip_options.width > 0 && pip_options.height > 0) {
-    // Use width and height if we have them both, and ensure that the size isn't
-    // too large.
-    gfx::Size requested_window_size(
-        base::saturated_cast<int>(pip_options.width),
-        base::saturated_cast<int>(pip_options.height));
-    gfx::Size window_size =
-        AdjustRequestedSizeIfNecessary(requested_window_size, display);
-
-#if !BUILDFLAG(IS_ANDROID)
-    if (is_calculating_initial_document_pip_size_) {
-      base::UmaHistogramBoolean(
-          "Media.DocumentPictureInPicture.RequestedLargeInitialSize",
-          requested_window_size != window_size);
-    }
-#endif  // !BUILDFLAG(IS_ANDROID)
-
-    // The pip options are the desired inner size, so we add any non-client size
-    // we need to convert to outer size by adding back the margin around the
-    // inner area.
-    window_size += excluded_margin;
-
+    // Use width and height if we have them both, but ensure it's within the
+    // required bounds.  Remember that the pip options are the desired inner
+    // size, so we add any non-client size we need to convert to outer size by
+    // adding back the margin around the inner area.
+    gfx::Size window_size(
+        base::saturated_cast<int>(pip_options.width + excluded_margin.width()),
+        base::saturated_cast<int>(pip_options.height +
+                                  excluded_margin.height()));
     window_size.SetToMin(GetMaximumWindowSize(display));
     window_size.SetToMax(minimum_outer_window_size);
     window_bounds = gfx::Rect(window_size);
@@ -508,8 +382,6 @@
     const display::Display& display) {
 #if !BUILDFLAG(IS_ANDROID)
   RecordDocumentPictureInPictureRequestedSizeMetrics(pip_options, display);
-  base::AutoReset<bool> auto_reset(&is_calculating_initial_document_pip_size_,
-                                   true);
 #endif  // !BUILDFLAG(IS_ANDROID)
 
   // Use an empty `excluded_margin`, which more or less guarantees that these
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
index b629247..8769fbb 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
@@ -129,18 +129,6 @@
   // document PiP window.
   static bool IsChildWebContents(content::WebContents*);
 
-  // When a website requests size `requested_size` for a document
-  // picture-in-picture window (either when creating the window or when resizing
-  // the window via resizeTo()/resizeBy() APIs), we restrict the maximum size
-  // we'll make the pip window (this is a smaller maximum than the maximum size
-  // the user can manually resize to). If the given `requested_size` is small
-  // enough, this just returns the requested size. Otherwise, this shrinks the
-  // requested size to fit within the constraints, and attempts to keep the
-  // aspect ratio the same.
-  static gfx::Size AdjustRequestedSizeIfNecessary(
-      const gfx::Size& requested_size,
-      const display::Display& display);
-
   // Returns the window bounds of the video picture-in-picture or the document
   // picture-in-picture if either of them is present.
   std::optional<gfx::Rect> GetPictureInPictureWindowBounds() const;
@@ -327,12 +315,6 @@
   // blocked.
   uint32_t number_of_existing_scoped_disallow_picture_in_pictures_ = 0;
 
-  // True if we're currently calculating document pip's initial size. Used to
-  // determine whether we should record the
-  // `Media.DocumentPictureInPicture.RequestedLargeInitialSize` metric and
-  // should be removed when that metric is removed.
-  bool is_calculating_initial_document_pip_size_ = false;
-
   std::unique_ptr<PictureInPictureWindowManagerUmaHelper> uma_helper_;
 #endif  //! BUILDFLAG(IS_ANDROID)
 
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager_unittest.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_manager_unittest.cc
index ef8e05f..1695cce 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager_unittest.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager_unittest.cc
@@ -132,6 +132,32 @@
 
 }  // namespace
 
+TEST_F(PictureInPictureWindowManagerTest, RespectsMinAndMaxSize) {
+  // The max window size should be 80% of the screen.
+  display::Display display(/*id=*/1, gfx::Rect(0, 0, 1000, 1000));
+  EXPECT_EQ(gfx::Size(800, 800),
+            PictureInPictureWindowManager::GetMaximumWindowSize(display));
+
+  // The initial bounds of the PiP window should respect that.
+  blink::mojom::PictureInPictureWindowOptions pip_options;
+  pip_options.width = 900;
+  pip_options.height = 900;
+  EXPECT_EQ(
+      gfx::Size(800, 800),
+      PictureInPictureWindowManager::GetInstance()
+          ->CalculateInitialPictureInPictureWindowBounds(pip_options, display)
+          .size());
+
+  // The minimum size should also be respected.
+  pip_options.width = 100;
+  pip_options.height = 500;
+  EXPECT_EQ(
+      gfx::Size(240, 500),
+      PictureInPictureWindowManager::GetInstance()
+          ->CalculateInitialPictureInPictureWindowBounds(pip_options, display)
+          .size());
+}
+
 TEST_F(PictureInPictureWindowManagerTest,
        ExitPictureInPictureReturnsFalseWhenThereIsNoWindow) {
   EXPECT_FALSE(
@@ -158,74 +184,6 @@
 }
 
 #if !BUILDFLAG(IS_ANDROID)
-TEST_F(PictureInPictureWindowManagerTest, RespectsMinAndMaxSize) {
-  // The max window size should be 80% of the screen.
-  display::Display display(/*id=*/1, gfx::Rect(0, 0, 1000, 1000));
-  EXPECT_EQ(gfx::Size(800, 800),
-            PictureInPictureWindowManager::GetMaximumWindowSize(display));
-
-  // The initial bounds of the PiP window should respect that.
-  blink::mojom::PictureInPictureWindowOptions pip_options;
-  pip_options.width = 900;
-  pip_options.height = 100;
-  EXPECT_EQ(
-      gfx::Size(800, 100),
-      PictureInPictureWindowManager::GetInstance()
-          ->CalculateInitialPictureInPictureWindowBounds(pip_options, display)
-          .size());
-
-  // Additionally, even if the given size is less than the absolute max, it
-  // should be forced to respect the maximum allowed area.
-  pip_options.width = 800;
-  pip_options.height = 800;
-  EXPECT_EQ(
-      gfx::Size(500, 500),
-      PictureInPictureWindowManager::GetInstance()
-          ->CalculateInitialPictureInPictureWindowBounds(pip_options, display)
-          .size());
-
-  // Additionally, even if the given size is less than the absolute max, it
-  // should be forced to respect the maximum allowed area.
-  pip_options.width = 800;
-  pip_options.height = 400;
-  EXPECT_EQ(
-      gfx::Size(707, 353),
-      PictureInPictureWindowManager::GetInstance()
-          ->CalculateInitialPictureInPictureWindowBounds(pip_options, display)
-          .size());
-
-  // If the requested width is so much larger than the height that maintaining
-  // the aspect ratio isn't possible within the min/max bounds, then it should
-  // keep the minimum height and expand the width to the maximum size.
-  pip_options.width = 10000;
-  pip_options.height = 400;
-  EXPECT_EQ(
-      gfx::Size(800, 52),
-      PictureInPictureWindowManager::GetInstance()
-          ->CalculateInitialPictureInPictureWindowBounds(pip_options, display)
-          .size());
-
-  // If the requested height is so much larger than the width that maintaining
-  // the aspect ratio isn't possible within the min/max bounds, then it should
-  // keep the minimum width and expand the height to the maximum size.
-  pip_options.width = 400;
-  pip_options.height = 10000;
-  EXPECT_EQ(
-      gfx::Size(240, 800),
-      PictureInPictureWindowManager::GetInstance()
-          ->CalculateInitialPictureInPictureWindowBounds(pip_options, display)
-          .size());
-
-  // The minimum size should also be respected.
-  pip_options.width = 100;
-  pip_options.height = 500;
-  EXPECT_EQ(
-      gfx::Size(240, 500),
-      PictureInPictureWindowManager::GetInstance()
-          ->CalculateInitialPictureInPictureWindowBounds(pip_options, display)
-          .size());
-}
-
 TEST_F(PictureInPictureWindowManagerTest, OnEnterDocumentPictureInPicture) {
   PictureInPictureWindowManager* picture_in_picture_window_manager =
       PictureInPictureWindowManager::GetInstance();
diff --git a/chrome/browser/predictors/lcp_critical_path_predictor/prewarm_http_disk_cache_manager.cc b/chrome/browser/predictors/lcp_critical_path_predictor/prewarm_http_disk_cache_manager.cc
index a4d4119..fe2501a1 100644
--- a/chrome/browser/predictors/lcp_critical_path_predictor/prewarm_http_disk_cache_manager.cc
+++ b/chrome/browser/predictors/lcp_critical_path_predictor/prewarm_http_disk_cache_manager.cc
@@ -8,8 +8,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "base/task/sequenced_task_runner.h"
-#include "base/trace_event/base_tracing.h"
-#include "base/trace_event/common/trace_event_common.h"
+#include "base/trace_event/trace_event.h"
 #include "chrome/browser/after_startup_task_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/isolation_info.h"
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 35b444c..82681d7c 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1131,6 +1131,10 @@
 inline constexpr char kPrivacySandboxFakeNoticeFirstSignOutTime[] =
     "privacy_sandbox.fake_notice.first_sign_out_time";
 
+// Deprecated 06/2025.
+inline constexpr char kStorageGarbageCollect[] =
+    "extensions.storage.garbagecollect";
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -1612,6 +1616,9 @@
   registry->RegisterTimeDeltaPref(kSyncPollInterval, base::TimeDelta());
   registry->RegisterDictionaryPref(kSharingVapidKey);
   registry->RegisterBooleanPref(kHasSeenWelcomePage, false);
+
+  // Deprecated 06/2025
+  registry->RegisterBooleanPref(kStorageGarbageCollect, false);
 }
 
 }  // namespace
@@ -2934,6 +2941,9 @@
   profile_prefs->ClearPref(kSharingVapidKey);
   profile_prefs->ClearPref(kHasSeenWelcomePage);
 
+  // Added 06/2025.
+  profile_prefs->ClearPref(kStorageGarbageCollect);
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 
diff --git a/chrome/browser/process_singleton_internal.cc b/chrome/browser/process_singleton_internal.cc
index b52ead7..3da34780 100644
--- a/chrome/browser/process_singleton_internal.cc
+++ b/chrome/browser/process_singleton_internal.cc
@@ -6,7 +6,7 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/tracing/protos/chrome_track_event.pbzero.h"
 #include "build/build_config.h"
 
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
index 68ca956b..1bc2d93 100644
--- a/chrome/browser/process_singleton_win.cc
+++ b/chrome/browser/process_singleton_win.cc
@@ -22,7 +22,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/win/registry.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/windows_version.h"
diff --git a/chrome/browser/profiles/profile_metrics_unittest.cc b/chrome/browser/profiles/profile_metrics_unittest.cc
index a601498..826cf6d 100644
--- a/chrome/browser/profiles/profile_metrics_unittest.cc
+++ b/chrome/browser/profiles/profile_metrics_unittest.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/files/file_path.h"
+#include "base/strings/to_string.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/time/time.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
diff --git a/chrome/browser/push_notification/server_client/push_notification_desktop_api_call_flow_impl_unittest.cc b/chrome/browser/push_notification/server_client/push_notification_desktop_api_call_flow_impl_unittest.cc
index 7b75310..5e6d528 100644
--- a/chrome/browser/push_notification/server_client/push_notification_desktop_api_call_flow_impl_unittest.cc
+++ b/chrome/browser/push_notification/server_client/push_notification_desktop_api_call_flow_impl_unittest.cc
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/functional/bind.h"
+#include "base/no_destructor.h"
 #include "base/test/task_environment.h"
 #include "net/base/net_errors.h"
 #include "net/base/url_util.h"
diff --git a/chrome/browser/renderer_host/javascript_optimizer_feature_browsertest.cc b/chrome/browser/renderer_host/javascript_optimizer_feature_browsertest.cc
index 23b0aa05..c01d8fa 100644
--- a/chrome/browser/renderer_host/javascript_optimizer_feature_browsertest.cc
+++ b/chrome/browser/renderer_host/javascript_optimizer_feature_browsertest.cc
@@ -15,6 +15,7 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_host_resolver.h"
+#include "content/public/test/test_utils.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -390,13 +391,13 @@
       content::ChildProcessSecurityPolicy::IsolatedOriginSource::
           USER_TRIGGERED));
 
-  if (content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites() &&
+  if (content::AreStrictSiteInstancesEnabled() &&
       !content::SiteIsolationPolicy::
           AreOriginKeyedProcessesEnabledByDefault()) {
-    // if a.com is isolated already (as is the case with full
-    // site isolation but not the case under origin isolation), the navigation
-    // to sub.a.com will be made in a SiteInstance with a "a.com" site URL,
-    // which will match a.com BLOCK rule.
+    // if a.com is isolated already (as is the case with full site isolation)
+    // or if DefaultSiteInstanceGroups are enabled, and origin isolation is not
+    // used, the navigation to sub.a.com will be made in a SiteInstance with a
+    // "a.com" site URL, which will match a.com BLOCK rule.
     EXPECT_TRUE(AreV8OptimizationsDisabledOnActiveWebContents());
   } else {
     // If nothing is isolated by default (like on Android), we'll navigate in a
diff --git a/chrome/browser/resources/privacy_sandbox/BUILD.gn b/chrome/browser/resources/privacy_sandbox/BUILD.gn
index c8012bd7..c2768783 100644
--- a/chrome/browser/resources/privacy_sandbox/BUILD.gn
+++ b/chrome/browser/resources/privacy_sandbox/BUILD.gn
@@ -50,10 +50,13 @@
     "three_ads_apis_notice.ts",
     "measurement_notice.html.ts",
     "measurement_notice.ts",
+    "base_dialog_learn_more.html.ts",
+    "base_dialog_learn_more.ts",
   ]
 
   css_files = [
     "base_dialog_app.css",
+    "base_dialog_learn_more.css",
     "base_dialog_styles.css",
     "privacy_sandbox_privacy_policy_dialog.css",
     "shared_style.css",
diff --git a/chrome/browser/resources/privacy_sandbox/base_dialog_learn_more.css b/chrome/browser/resources/privacy_sandbox/base_dialog_learn_more.css
new file mode 100644
index 0000000..4ddcee75
--- /dev/null
+++ b/chrome/browser/resources/privacy_sandbox/base_dialog_learn_more.css
@@ -0,0 +1,26 @@
+/* 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. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #scheme=relative
+ * #css_wrapper_metadata_end */
+
+.learn-more {
+  border-bottom: 1px solid #D3E3FD;
+}
+
+@media (prefers-color-scheme: dark) {
+  .learn-more {
+    border-bottom: 1px solid #5E5E5E; /* Dark mode border */
+  }
+}
+
+cr-expand-button {
+  --cr-section-vertical-padding: 16px;
+}
+
+.cr-secondary-text{
+  margin-left: 4px;
+}
diff --git a/chrome/browser/resources/privacy_sandbox/base_dialog_learn_more.html.ts b/chrome/browser/resources/privacy_sandbox/base_dialog_learn_more.html.ts
new file mode 100644
index 0000000..ad4d529bf
--- /dev/null
+++ b/chrome/browser/resources/privacy_sandbox/base_dialog_learn_more.html.ts
@@ -0,0 +1,21 @@
+// 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 {html} from '//resources/lit/v3_0/lit.rollup.js';
+
+import type {BaseDialogLearnMore} from './base_dialog_learn_more.js';
+
+export function getHtml(this: BaseDialogLearnMore) {
+  return html`<!--_html_template_start_-->
+<div class="learn-more">
+  <cr-expand-button ?expanded="${this.expanded_}"
+      @expanded-changed="${this.onExpandedChanged_}">
+    <div class="cr-secondary-text">${this.title}</div>
+  </cr-expand-button>
+  <cr-collapse id="collapse" ?opened="${this.expanded_}">
+    <slot></slot>
+  </cr-collapse>
+</div>
+<!--_html_template_end_-->`;
+}
diff --git a/chrome/browser/resources/privacy_sandbox/base_dialog_learn_more.ts b/chrome/browser/resources/privacy_sandbox/base_dialog_learn_more.ts
new file mode 100644
index 0000000..c4152c0
--- /dev/null
+++ b/chrome/browser/resources/privacy_sandbox/base_dialog_learn_more.ts
@@ -0,0 +1,58 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import '//resources/cr_elements/cr_collapse/cr_collapse.js';
+import '//resources/cr_elements/cr_expand_button/cr_expand_button.js';
+
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
+import type {CrCollapseElement} from 'chrome://resources/cr_elements/cr_collapse/cr_collapse.js';
+
+import {getCss} from './base_dialog_learn_more.css.js';
+import {getHtml} from './base_dialog_learn_more.html.js';
+
+export interface BaseDialogLearnMore {
+  $: {
+    collapse: CrCollapseElement,
+  };
+}
+
+export class BaseDialogLearnMore extends CrLitElement {
+  static get is() {
+    return 'base-dialog-learn-more';
+  }
+
+  static override get styles() {
+    return getCss();
+  }
+
+  override render() {
+    return getHtml.bind(this)();
+  }
+
+  static override get properties() {
+    return {
+      expanded_: {type: Boolean, notify: true},
+      title: {type: String},
+    };
+  }
+
+  accessor expanded_: boolean = false;
+
+  protected onExpandedChanged_(e: CustomEvent<{value: boolean}>) {
+    this.expanded_ = e.detail.value;
+    if (this.expanded_) {
+      requestAnimationFrame(() => {
+        this.$.collapse.scrollIntoView({block: 'start', behavior: 'smooth'});
+      });
+    }
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'base-dialog-learn-more': BaseDialogLearnMore;
+  }
+}
+
+customElements.define(BaseDialogLearnMore.is, BaseDialogLearnMore);
diff --git a/chrome/browser/safe_browsing/notification_content_detection/notification_content_detection_util_unittest.cc b/chrome/browser/safe_browsing/notification_content_detection/notification_content_detection_util_unittest.cc
index b666f80..a756595d 100644
--- a/chrome/browser/safe_browsing/notification_content_detection/notification_content_detection_util_unittest.cc
+++ b/chrome/browser/safe_browsing/notification_content_detection/notification_content_detection_util_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/safe_browsing/notification_content_detection/notification_content_detection_util.h"
 
 #include "base/json/json_string_value_serializer.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
diff --git a/chrome/browser/serial/serial_chooser_context.cc b/chrome/browser/serial/serial_chooser_context.cc
index 63ebcb5..6199abe2 100644
--- a/chrome/browser/serial/serial_chooser_context.cc
+++ b/chrome/browser/serial/serial_chooser_context.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 "chrome/browser/serial/serial_chooser_context.h"
 
 #include <string_view>
@@ -15,6 +10,7 @@
 #include "base/base64.h"
 #include "base/containers/contains.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/numerics/byte_conversions.h"
 #include "base/observer_list.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -74,9 +70,11 @@
     return base::UnguessableToken();
   }
 
-  const uint64_t* data = reinterpret_cast<const uint64_t*>(buffer.data());
+  base::span<const uint8_t> byte_buffer = base::as_byte_span(buffer);
+  uint64_t high = base::U64FromLittleEndian(byte_buffer.first<8>());
+  uint64_t low = base::U64FromLittleEndian(byte_buffer.subspan<8, 8>());
   std::optional<base::UnguessableToken> token =
-      base::UnguessableToken::Deserialize(data[0], data[1]);
+      base::UnguessableToken::Deserialize(high, low);
   if (!token.has_value()) {
     return base::UnguessableToken();
   }
diff --git a/chrome/browser/storage/shared_storage_browsertest.cc b/chrome/browser/storage/shared_storage_browsertest.cc
index 31dfe6b3..51ce5de 100644
--- a/chrome/browser/storage/shared_storage_browsertest.cc
+++ b/chrome/browser/storage/shared_storage_browsertest.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/to_string.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
diff --git a/chrome/browser/support_tool/ash/chrome_user_logs_data_collector_unittest.cc b/chrome/browser/support_tool/ash/chrome_user_logs_data_collector_unittest.cc
index 3918c5d9..0a418fd 100644
--- a/chrome/browser/support_tool/ash/chrome_user_logs_data_collector_unittest.cc
+++ b/chrome/browser/support_tool/ash/chrome_user_logs_data_collector_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_file_util.h"
 #include "base/test/test_future.h"
+#include "base/threading/thread_restrictions.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/support_tool/data_collector.h"
 #include "chrome/test/base/fake_profile_manager.h"
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/autofill/android/internal/BUILD.gn
index 3b5c233..ba9436c 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/BUILD.gn
@@ -52,6 +52,7 @@
     "java/res/layout/touch_to_fill_payment_method_footer_item.xml",
     "java/res/layout/touch_to_fill_payment_method_header_item.xml",
     "java/res/layout/touch_to_fill_terms_label_sheet_item.xml",
+    "java/res/layout/touch_to_fill_wallet_settings_button.xml",
     "java/res/values/dimens.xml",
   ]
 }
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_wallet_settings_button.xml b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_wallet_settings_button.xml
new file mode 100644
index 0000000..912a6be
--- /dev/null
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/res/layout/touch_to_fill_wallet_settings_button.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+
+<org.chromium.ui.widget.ButtonCompat
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:descendantFocusability="blocksDescendants"
+    android:id="@+id/touch_to_fill_wallet_settings_button"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="0dp"
+    android:layout_marginHorizontal="8dp"
+    android:minHeight="48dp"
+    android:gravity="center"
+    android:ellipsize="end"
+    android:singleLine="true"
+    style="@style/TextButton"/>
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java
index a9f1444..df9b773 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java
@@ -31,6 +31,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodMediator.TOUCH_TO_FILL_NUMBER_OF_IBANS_SHOWN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodMediator.TOUCH_TO_FILL_NUMBER_OF_LOYALTY_CARDS_SHOWN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ButtonProperties.ON_CLICK_ACTION;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ButtonProperties.TEXT_ID;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.CreditCardSuggestionProperties.APPLY_DEACTIVATED_STYLE;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.CreditCardSuggestionProperties.FIRST_LINE_LABEL;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.CreditCardSuggestionProperties.MAIN_TEXT;
@@ -55,6 +56,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.IBAN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.LOYALTY_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.TERMS_LABEL;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.WALLET_SETTINGS_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.LOYALTY_CARD_NUMBER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.MERCHANT_NAME;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.ON_LOYALTY_CARD_CLICK_ACTION;
@@ -913,10 +915,30 @@
         assertThat(loyaltyCardModel.get(MERCHANT_NAME), is(LOYALTY_CARD_1.getMerchantName()));
 
         assertThat(getModelsOfType(itemList, FILL_BUTTON).size(), is(1));
+        assertThat(getModelsOfType(itemList, WALLET_SETTINGS_BUTTON).size(), is(1));
+        PropertyModel walletSettingButtonModel =
+                getModelsOfType(itemList, WALLET_SETTINGS_BUTTON).get(0);
+        assertThat(
+                walletSettingButtonModel.get(TEXT_ID),
+                is(R.string.autofill_loyalty_card_wallet_settings_button));
         assertThat(getModelsOfType(itemList, FOOTER).size(), is(1));
     }
 
     @Test
+    public void testWalletSettingsButtonRedirectsToSettings() {
+        mCoordinator.showLoyaltyCards(
+                List.of(LOYALTY_CARD_1), List.of(LOYALTY_CARD_1), /* firstTimeUsage= */ true);
+
+        ModelList itemList = mTouchToFillPaymentMethodModel.get(SHEET_ITEMS);
+        assertThat(getModelsOfType(itemList, WALLET_SETTINGS_BUTTON).size(), is(1));
+        PropertyModel walletSettingButtonModel =
+                getModelsOfType(itemList, WALLET_SETTINGS_BUTTON).get(0);
+        walletSettingButtonModel.get(ON_CLICK_ACTION).run();
+
+        verify(mDelegateMock).showGoogleWalletSettings();
+    }
+
+    @Test
     public void testShowOneLoyaltyCard() throws TimeoutException {
         HistogramWatcher histogramWatcher =
                 HistogramWatcher.newSingleRecordWatcher(
@@ -941,6 +963,7 @@
         assertThat(loyaltyCardModel.get(MERCHANT_NAME), is(LOYALTY_CARD_1.getMerchantName()));
 
         assertThat(getModelsOfType(itemList, FILL_BUTTON).size(), is(1));
+        assertThat(getModelsOfType(itemList, WALLET_SETTINGS_BUTTON).size(), is(0));
         assertThat(getModelsOfType(itemList, FOOTER).size(), is(1));
     }
 
@@ -977,6 +1000,7 @@
         assertThat(loyaltyCardModel2.get(MERCHANT_NAME), is(LOYALTY_CARD_2.getMerchantName()));
 
         assertThat(getModelsOfType(itemList, FILL_BUTTON).size(), is(0));
+        assertThat(getModelsOfType(itemList, WALLET_SETTINGS_BUTTON).size(), is(0));
         assertThat(getModelsOfType(itemList, FOOTER).size(), is(1));
     }
 
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java
index 8972c11..9e3b91e 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java
@@ -14,6 +14,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.IBAN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.LOYALTY_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.TERMS_LABEL;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.WALLET_SETTINGS_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.VISIBLE;
 
@@ -137,7 +138,11 @@
         adapter.registerType(
                 FILL_BUTTON,
                 TouchToFillPaymentMethodViewBinder::createFillButtonView,
-                TouchToFillPaymentMethodViewBinder::bindFillButtonView);
+                TouchToFillPaymentMethodViewBinder::bindButtonView);
+        adapter.registerType(
+                WALLET_SETTINGS_BUTTON,
+                TouchToFillPaymentMethodViewBinder::createWalletSettingsButtonView,
+                TouchToFillPaymentMethodViewBinder::bindButtonView);
         adapter.registerType(
                 FOOTER,
                 TouchToFillPaymentMethodViewBinder::createFooterItemView,
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodMediator.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodMediator.java
index a57fbe07..0098e72 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodMediator.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodMediator.java
@@ -34,6 +34,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.IBAN;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.LOYALTY_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.TERMS_LABEL;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.ItemType.WALLET_SETTINGS_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.LOYALTY_CARD_ICON;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.LOYALTY_CARD_NUMBER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillPaymentMethodProperties.LoyaltyCardProperties.MERCHANT_NAME;
@@ -319,6 +320,10 @@
                                     () -> this.onSelectedLoyaltyCard(mLoyaltyCards.get(0)))));
         }
 
+        if (firstTimeUsage) {
+            sheetItems.add(new ListItem(WALLET_SETTINGS_BUTTON, createWalletSettingsButtonModel()));
+        }
+
         sheetItems.add(0, buildHeaderForLoyaltyCards(firstTimeUsage));
         sheetItems.add(buildFooterForLoyaltyCards());
 
@@ -484,6 +489,13 @@
                 .build();
     }
 
+    private PropertyModel createWalletSettingsButtonModel() {
+        return new PropertyModel.Builder(ButtonProperties.ALL_KEYS)
+                .with(TEXT_ID, R.string.autofill_loyalty_card_wallet_settings_button)
+                .with(ON_CLICK_ACTION, mDelegate::showGoogleWalletSettings)
+                .build();
+    }
+
     private ListItem buildTermsLabel(boolean cardBenefitsTermsAvailable) {
         return new ListItem(
                 TERMS_LABEL,
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodProperties.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodProperties.java
index 4d219ee2..6f3b900 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodProperties.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodProperties.java
@@ -47,11 +47,14 @@
         // method available.
         int FILL_BUTTON = 4;
 
+        // A button that redirects the user to the Wallet settings in Chrome.
+        int WALLET_SETTINGS_BUTTON = 5;
+
         // A footer section containing additional actions.
-        int FOOTER = 5;
+        int FOOTER = 6;
 
         // A section with a terms label is present when card benefits are available.
-        int TERMS_LABEL = 6;
+        int TERMS_LABEL = 7;
     }
 
     /** Metadata associated with a card's image. */
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodView.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodView.java
index db49eb1..cc97044 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodView.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodView.java
@@ -44,6 +44,7 @@
                 case ItemType.HEADER: // Fallthrough.
                 case ItemType.FOOTER: // Fallthrough.
                 case ItemType.FILL_BUTTON:
+                case ItemType.WALLET_SETTINGS_BUTTON:
                 case ItemType.TERMS_LABEL:
                     return true;
                 case ItemType.CREDIT_CARD:
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBinder.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBinder.java
index e5db0e6..0c7249b0 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBinder.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodViewBinder.java
@@ -39,6 +39,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -290,20 +291,51 @@
         }
     }
 
-    static View createFillButtonView(ViewGroup parent) {
-        View buttonView =
-                LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.touch_to_fill_fill_button, parent, false);
+    /**
+     * Factory used to create a new "Continue" or "Autofill" button that fills in data into the
+     * focused field.
+     *
+     * @param parent The parent {@link ViewGroup} of the new item.
+     */
+    static Button createFillButtonView(ViewGroup parent) {
+        Button buttonView =
+                (Button)
+                        LayoutInflater.from(parent.getContext())
+                                .inflate(R.layout.touch_to_fill_fill_button, parent, false);
         AutofillUiUtils.setFilterTouchForSecurity(buttonView);
         return buttonView;
     }
 
-    static void bindFillButtonView(PropertyModel model, View view, PropertyKey propertyKey) {
+    /**
+     * Factory used to create a new "Wallet settings" button that redirects the user to the
+     * corresponding Chrome settings page.
+     *
+     * @param parent The parent {@link ViewGroup} of the new item.
+     */
+    static Button createWalletSettingsButtonView(ViewGroup parent) {
+        Button buttonView =
+                (Button)
+                        LayoutInflater.from(parent.getContext())
+                                .inflate(
+                                        R.layout.touch_to_fill_wallet_settings_button,
+                                        parent,
+                                        false);
+        AutofillUiUtils.setFilterTouchForSecurity(buttonView);
+        return buttonView;
+    }
+
+    /**
+     * Called whenever a property in the given model changes. It updates the given view accordingly.
+     *
+     * @param model The observed {@link PropertyModel}. Its data need to be reflected in the view.
+     * @param button The {@link Button} from the bottom sheet to update.
+     * @param key The {@link PropertyKey} which changed.
+     */
+    static void bindButtonView(PropertyModel model, Button button, PropertyKey propertyKey) {
         if (propertyKey == TEXT_ID) {
-            TextView buttonTitleText = view.findViewById(R.id.touch_to_fill_button_title);
-            buttonTitleText.setText(model.get(TEXT_ID));
+            button.setText(model.get(TEXT_ID));
         } else if (propertyKey == ON_CLICK_ACTION) {
-            view.setOnClickListener(unusedView -> model.get(ON_CLICK_ACTION).run());
+            button.setOnClickListener(unusedView -> model.get(ON_CLICK_ACTION).run());
         } else {
             assert false : "Unhandled update to property:" + propertyKey;
         }
diff --git a/chrome/browser/touch_to_fill/autofill/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodComponent.java b/chrome/browser/touch_to_fill/autofill/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodComponent.java
index cf569d8..3b9edad 100644
--- a/chrome/browser/touch_to_fill/autofill/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodComponent.java
+++ b/chrome/browser/touch_to_fill/autofill/android/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodComponent.java
@@ -34,6 +34,12 @@
         void showPaymentMethodSettings();
 
         /**
+         * Causes navigation to the payment methods settings page and scrolls to the loyalty card
+         * settings preference.
+         */
+        void showGoogleWalletSettings();
+
+        /**
          * Called when the user selects a card.
          *
          * @param uniqueId A backend id of the card.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index e568c086..7543f2d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -948,6 +948,8 @@
       sources += [
         "android/extensions/extension_keybinding_registry_android.cc",
         "android/extensions/extension_keybinding_registry_android.h",
+        "android/toolbar/extension_action_popup_contents.cc",
+        "android/toolbar/extension_action_popup_contents.h",
         "android/toolbar/extension_actions_bridge.cc",
         "android/toolbar/extension_actions_bridge.h",
         "android/toolbar/extension_actions_bridge_factory.cc",
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
index c2b2384e2..703dce4 100644
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -239,12 +239,22 @@
       "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionButtonViewBinder.java",
       "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListCoordinator.java",
       "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediator.java",
+      "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopup.java",
+      "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopupContents.java",
       "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionsBridge.java",
       "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManagerImpl.java",
       "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuButtonCoordinator.java",
     ]
 
     srcjar_deps += [ "//extensions/browser:extension_action_enums" ]
+
+    deps += [
+      "//base/version_info/android:version_constants_java",
+      "//components/embedder_support/android:content_view_java",
+      "//components/embedder_support/android:web_contents_delegate_java",
+      "//components/thin_webview:factory_java",
+      "//components/thin_webview:java",
+    ]
   }
 }
 
@@ -260,6 +270,7 @@
   if (enable_extensions_core) {
     sources += [
       "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionAction.java",
+      "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopupContents.java",
       "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionsBridge.java",
     ]
   }
diff --git a/chrome/browser/ui/android/toolbar/DEPS b/chrome/browser/ui/android/toolbar/DEPS
index d6daa04..ae543b21 100644
--- a/chrome/browser/ui/android/toolbar/DEPS
+++ b/chrome/browser/ui/android/toolbar/DEPS
@@ -8,6 +8,7 @@
   "+components/embedder_support/android",
   "+components/omnibox/browser/android",
   "+components/security_state/content/android",
+  "+components/thin_webview",
   "+content/public/android",
   "+extensions/android",
 ]
diff --git a/chrome/browser/ui/android/toolbar/extension_action_popup_contents.cc b/chrome/browser/ui/android/toolbar/extension_action_popup_contents.cc
new file mode 100644
index 0000000..d2866545
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/extension_action_popup_contents.cc
@@ -0,0 +1,117 @@
+// 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/android/toolbar/extension_action_popup_contents.h"
+
+#include "base/android/jni_string.h"
+#include "base/notimplemented.h"
+#include "chrome/browser/extensions/extension_view_host.h"
+#include "chrome/browser/extensions/extension_view_host_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_action.h"
+#include "extensions/browser/extension_action_manager.h"
+#include "extensions/browser/extension_registry.h"
+
+// Must come after all headers that specialize FromJniType() / ToJniType().
+#include "chrome/browser/ui/android/toolbar/jni_headers/ExtensionActionPopupContents_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+using content::RenderFrameHost;
+using content::WebContents;
+using extensions::Extension;
+using extensions::ExtensionAction;
+using extensions::ExtensionActionManager;
+using extensions::ExtensionRegistry;
+using extensions::ExtensionViewHost;
+using extensions::ExtensionViewHostFactory;
+
+ExtensionActionPopupContents::ExtensionActionPopupContents(
+    std::unique_ptr<ExtensionViewHost> host)
+    : host_(std::move(host)) {
+  java_object_ = Java_ExtensionActionPopupContents_Constructor(
+      AttachCurrentThread(), reinterpret_cast<jlong>(this),
+      host_->host_contents()->GetJavaWebContents());
+  host_->set_view(this);
+}
+
+ExtensionActionPopupContents::~ExtensionActionPopupContents() = default;
+
+ScopedJavaLocalRef<jobject> ExtensionActionPopupContents::GetJavaObject() {
+  return java_object_.AsLocalRef(AttachCurrentThread());
+}
+
+void ExtensionActionPopupContents::ResizeDueToAutoResize(
+    content::WebContents* web_contents,
+    const gfx::Size& new_size) {
+  Java_ExtensionActionPopupContents_resizeDueToAutoResize(
+      AttachCurrentThread(), java_object_, new_size.width(), new_size.height());
+}
+
+void ExtensionActionPopupContents::RenderFrameCreated(
+    RenderFrameHost* render_frame_host) {
+  NOTIMPLEMENTED();
+}
+
+bool ExtensionActionPopupContents::HandleKeyboardEvent(
+    content::WebContents* source,
+    const input::NativeWebKeyboardEvent& event) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void ExtensionActionPopupContents::OnLoaded() {
+  Java_ExtensionActionPopupContents_onLoaded(AttachCurrentThread(),
+                                             java_object_);
+}
+
+void ExtensionActionPopupContents::Destroy(JNIEnv* env) {
+  delete this;
+}
+
+void ExtensionActionPopupContents::LoadInitialPage(JNIEnv* env) {
+  host_->CreateRendererSoon();
+}
+
+// JNI method to create an ExtensionActionPopupContents instance.
+// This is called from the Java side to initiate the display of an extension
+// popup.
+static ScopedJavaLocalRef<jobject> JNI_ExtensionActionPopupContents_Create(
+    JNIEnv* env,
+    Profile* profile,
+    std::string& action_id,
+    jint tab_id) {
+  ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
+  DCHECK(registry);
+
+  ExtensionActionManager* manager = ExtensionActionManager::Get(profile);
+  DCHECK(manager);
+
+  const Extension* extension =
+      registry->enabled_extensions().GetByID(action_id);
+  DCHECK(extension);
+
+  ExtensionAction* action = manager->GetExtensionAction(*extension);
+  DCHECK(action);
+
+  GURL popup_url = action->GetPopupUrl(tab_id);
+
+  std::unique_ptr<ExtensionViewHost> host =
+      ExtensionViewHostFactory::CreatePopupHost(popup_url, profile);
+  DCHECK(host);
+
+  // The ExtensionActionPopupContents C++ object's lifetime is managed by its
+  // Java counterpart. The Java object holds a pointer to this C++ instance.
+  // When the Java side is finished with the popup, it will explicitly call
+  // a 'destroy()' method on its Java object, which in turn calls the native
+  // ExtensionActionPopupContents::Destroy() method, leading to the deletion
+  // of this C++ object. Therefore, 'new' is used here, and ownership is
+  // effectively passed to the Java-controlled lifecycle.
+  ExtensionActionPopupContents* popup =
+      new ExtensionActionPopupContents(std::move(host));
+  return popup->GetJavaObject();
+}
diff --git a/chrome/browser/ui/android/toolbar/extension_action_popup_contents.h b/chrome/browser/ui/android/toolbar/extension_action_popup_contents.h
new file mode 100644
index 0000000..9851877
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/extension_action_popup_contents.h
@@ -0,0 +1,66 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ANDROID_TOOLBAR_EXTENSION_ACTION_POPUP_CONTENTS_H_
+#define CHROME_BROWSER_UI_ANDROID_TOOLBAR_EXTENSION_ACTION_POPUP_CONTENTS_H_
+
+#include <memory>
+
+#include "base/android/jni_android.h"
+#include "chrome/browser/extensions/extension_view.h"
+
+namespace content {
+class RenderFrameHost;
+}
+
+namespace extensions {
+class ExtensionViewHost;
+}
+
+// ExtensionActionPopupContents is the native C++ class responsible for managing
+// the content of an extension's popup displayed on Android. An extension popup
+// is typically a small HTML page an extension can show when its action icon
+// is clicked. This class bridges the C++ extensions system with the Java UI.
+//
+// Lifetime Management:
+// An instance of this C++ class is created when its Java counterpart
+// (ExtensionActionPopupContents.java) requests it via a JNI call (specifically,
+// JNI_ExtensionActionPopupContents_Create). The C++ object's lifetime is tied
+// to its Java peer. The Java object holds a native pointer (jlong) to this C++
+// instance. When the Java object is no longer needed (e.g. the popup is
+// closed), its `destroy()` method is called. This, in turn, calls the native
+// `Destroy()` method on this C++ object, which then calls `delete this`.
+class ExtensionActionPopupContents : public extensions::ExtensionView {
+ public:
+  explicit ExtensionActionPopupContents(
+      std::unique_ptr<extensions::ExtensionViewHost> popup_host);
+  ExtensionActionPopupContents(const ExtensionActionPopupContents&) = delete;
+  ExtensionActionPopupContents& operator=(const ExtensionActionPopupContents&) =
+      delete;
+  ~ExtensionActionPopupContents() override;
+
+  // Returns a local JNI reference to the Java counterpart of this object.
+  base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
+
+  // ExtensionView:
+  void ResizeDueToAutoResize(content::WebContents* web_contents,
+                             const gfx::Size& new_size) override;
+  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+  bool HandleKeyboardEvent(content::WebContents* source,
+                           const input::NativeWebKeyboardEvent& event) override;
+  void OnLoaded() override;
+
+  // Called from Java when the Java counterpart is being destroyed.
+  void Destroy(JNIEnv* env);
+
+  // Called from Java to trigger the loading of the popup's initial URL in the
+  // hosted WebContents.
+  void LoadInitialPage(JNIEnv* env);
+
+ private:
+  std::unique_ptr<extensions::ExtensionViewHost> host_;
+  base::android::ScopedJavaGlobalRef<jobject> java_object_;
+};
+
+#endif  // CHROME_BROWSER_UI_ANDROID_TOOLBAR_EXTENSION_ACTION_POPUP_CONTENTS_H_
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListCoordinator.java
index 5ff0ad77..e1e83fc 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListCoordinator.java
@@ -17,6 +17,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.chrome.browser.toolbar.extensions.ExtensionActionButtonProperties.ListItemType;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.listmenu.ListMenuButton;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.ViewGroupAdapter;
@@ -34,10 +35,13 @@
     public ExtensionActionListCoordinator(
             Context context,
             LinearLayout container,
+            WindowAndroid windowAndroid,
             ObservableSupplier<Profile> profileSupplier,
             ObservableSupplier<Tab> currentTabSupplier) {
         ModelList models = new ModelList();
-        mMediator = new ExtensionActionListMediator(models, profileSupplier, currentTabSupplier);
+        mMediator =
+                new ExtensionActionListMediator(
+                        context, windowAndroid, models, profileSupplier, currentTabSupplier);
         mAdapter =
                 new ViewGroupAdapter.Builder(container, models)
                         .registerType(
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediator.java
index 99be868..16cff81 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediator.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.toolbar.extensions;
 
+import android.content.Context;
 import android.graphics.Bitmap;
 import android.view.View;
 
@@ -19,6 +20,7 @@
 import org.chromium.chrome.browser.toolbar.extensions.ExtensionActionButtonProperties.ListItemType;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.extensions.ShowAction;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.ModelListAdapter;
@@ -35,9 +37,12 @@
 class ExtensionActionListMediator implements Destroyable {
     private static final String TAG = "EALMediator";
 
+    private final Context mContext;
+    private final WindowAndroid mWindowAndroid;
     private final ModelList mModels;
     private final ObservableSupplier<Profile> mProfileSupplier;
     private final ObservableSupplier<Tab> mCurrentTabSupplier;
+
     private final Callback<Profile> mProfileUpdatedCallback = this::onProfileUpdated;
     private final Callback<Tab> mTabChangedCallback = this::onTabChanged;
     private final ActionsObserver mActionsObserver = new ActionsObserver();
@@ -47,11 +52,16 @@
     @Nullable private Profile mProfile;
     @Nullable private ExtensionActionsBridge mExtensionActionsBridge;
     @Nullable private Tab mCurrentTab;
+    @Nullable private ExtensionActionPopup mCurrentPopup;
 
     public ExtensionActionListMediator(
+            Context context,
+            WindowAndroid windowAndroid,
             ModelList models,
             ObservableSupplier<Profile> profileSupplier,
             ObservableSupplier<Tab> currentTabSupplier) {
+        mContext = context;
+        mWindowAndroid = windowAndroid;
         mModels = models;
         mProfileSupplier = profileSupplier;
         mCurrentTabSupplier = currentTabSupplier;
@@ -68,6 +78,8 @@
         mCurrentTabSupplier.removeObserver(mTabChangedCallback);
         mProfileSupplier.removeObserver(mProfileUpdatedCallback);
 
+        closePopup();
+        assert mCurrentPopup == null;
         mCurrentTab = null;
         mExtensionActionsBridge = null;
         mProfile = null;
@@ -80,6 +92,9 @@
             return;
         }
 
+        closePopup();
+        assert mCurrentPopup == null;
+
         if (mExtensionActionsBridge != null) {
             mExtensionActionsBridge.removeObserver(mActionsObserver);
         }
@@ -110,6 +125,9 @@
         if (tab == mCurrentTab) {
             return;
         }
+
+        closePopup();
+
         if (tab == null) {
             // The current tab can be null when a non-tab UI is shown (e.g. tab switcher). In this
             // case, we do not bother refreshing actions as they're hidden anyway. We do not set
@@ -129,7 +147,7 @@
         maybeUpdateAllActions();
     }
 
-    private void onPrimaryClick(View unused_buttonView, String actionId) {
+    private void onPrimaryClick(View buttonView, String actionId) {
         if (mExtensionActionsBridge == null || mCurrentTab == null) {
             return;
         }
@@ -145,7 +163,7 @@
             case ShowAction.NONE:
                 break;
             case ShowAction.SHOW_POPUP:
-                Log.e(TAG, "Extension popups are not implemented yet");
+                openPopup(buttonView, actionId);
                 break;
             case ShowAction.TOGGLE_SIDE_PANEL:
                 Log.e(TAG, "Extension side panels are not implemented yet");
@@ -153,6 +171,37 @@
         }
     }
 
+    private void openPopup(View buttonView, String actionId) {
+        // TODO(crbug.com/385987224): Do not open a popup again when the user clicks the action
+        // button while its popup is open.
+        closePopup();
+
+        if (mProfile == null || mCurrentTab == null) {
+            return;
+        }
+        int tabId = mCurrentTab.getId();
+
+        ExtensionActionPopupContents contents =
+                ExtensionActionPopupContents.create(mProfile, actionId, tabId);
+        assert mCurrentPopup == null;
+        mCurrentPopup =
+                new ExtensionActionPopup(mContext, mWindowAndroid, buttonView, actionId, contents);
+        mCurrentPopup.loadInitialPage();
+        mCurrentPopup.addOnDismissListener(this::closePopup);
+    }
+
+    private void closePopup() {
+        if (mCurrentPopup == null) {
+            return;
+        }
+        assert mExtensionActionsBridge != null;
+
+        // Clear mCurrentPopup now to avoid calling closePopup recursively via OnDismissListener.
+        ExtensionActionPopup popup = mCurrentPopup;
+        mCurrentPopup = null;
+        popup.destroy();
+    }
+
     private void maybeUpdateAllActions() {
         if (mProfile == null || mExtensionActionsBridge == null || mCurrentTab == null) {
             mModels.clear();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediatorTest.java
index 416e3d1e..a153098 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediatorTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediatorTest.java
@@ -13,6 +13,7 @@
 import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
 
+import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -35,6 +36,7 @@
 import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.toolbar.extensions.ExtensionActionButtonProperties.ListItemType;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 
@@ -54,8 +56,10 @@
     private static final Bitmap ICON_WHITE = createSimpleIcon(Color.YELLOW);
 
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Mock private Context mContext;
     @Mock private Profile mProfile;
     @Mock private ExtensionActionsBridge.Natives mActionsBridgeJniMock;
+    @Mock private WindowAndroid mWindowAndroid;
 
     private ExtensionActionsBridge mActionsBridge;
     private MockTab mTab1;
@@ -106,7 +110,9 @@
         mProfileSupplier = new ObservableSupplierImpl<>();
         mCurrentTabSupplier = new ObservableSupplierImpl<>();
         mModels = new ModelList();
-        mMediator = new ExtensionActionListMediator(mModels, mProfileSupplier, mCurrentTabSupplier);
+        mMediator =
+                new ExtensionActionListMediator(
+                        mContext, mWindowAndroid, mModels, mProfileSupplier, mCurrentTabSupplier);
 
         // Wait for the main thread to settle.
         shadowOf(Looper.getMainLooper()).idle();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopup.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopup.java
new file mode 100644
index 0000000..0c99ffc
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopup.java
@@ -0,0 +1,152 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.toolbar.extensions;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.PopupWindow.OnDismissListener;
+
+import org.chromium.base.lifetime.Destroyable;
+import org.chromium.base.version_info.VersionInfo;
+import org.chromium.build.NullUtil;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.components.embedder_support.view.ContentView;
+import org.chromium.components.thinwebview.ThinWebView;
+import org.chromium.components.thinwebview.ThinWebViewConstraints;
+import org.chromium.components.thinwebview.ThinWebViewFactory;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.ViewAndroidDelegate;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.widget.AnchoredPopupWindow;
+import org.chromium.ui.widget.ViewRectProvider;
+
+/**
+ * Manages the display of an extension action's popup UI.
+ *
+ * <p>This class is responsible for creating and managing an {@link AnchoredPopupWindow} that hosts
+ * a {@link ThinWebView} rendering the extension's popup HTML. It owns {@link
+ * ExtensionActionPopupContents} which handles the native interactions and WebContents for the
+ * popup.
+ *
+ * <p>The popup's size is determined by the popup content, constrained by hard-coded limits, and
+ * managed by the nested {@link ContentsFrame}.
+ */
+@NullMarked
+class ExtensionActionPopup implements Destroyable {
+    /** The ID of the extension action this popup is associated with. */
+    private final String mActionId;
+
+    /** The content manager for the popup, bridging to native. */
+    private final ExtensionActionPopupContents mContents;
+
+    /** The ThinWebView component that renders the extension's HTML content. */
+    private final ThinWebView mThinWebView;
+
+    /** The View containing the popup contents. Content sizing can be applied to this view. */
+    private final View mMainView;
+
+    /** The PopupWindow that is displayed on the screen, anchored to a view. */
+    private final AnchoredPopupWindow mPopupWindow;
+
+    /**
+     * Constructs an ExtensionActionPopup.
+     *
+     * @param context The {@link Context} to use for creating views.
+     * @param windowAndroid The {@link WindowAndroid} for the current activity.
+     * @param anchorView The {@link View} to which the popup will be anchored.
+     * @param actionId The ID of the extension action.
+     * @param contents The {@link ExtensionActionPopupContents} instance that manages the
+     *     WebContents and native communication for this popup. The new {@link ExtensionActionPopup}
+     *     instance takes ownership of the provided {@code contents} and will be responsible for
+     *     calling its {@code destroy()} method.
+     */
+    public ExtensionActionPopup(
+            Context context,
+            WindowAndroid windowAndroid,
+            View anchorView,
+            String actionId,
+            ExtensionActionPopupContents contents) {
+        mActionId = actionId;
+        mContents = contents;
+
+        WebContents webContents = contents.getWebContents();
+
+        ContentView contentView = ContentView.createContentView(context, webContents);
+
+        webContents.setDelegates(
+                VersionInfo.getProductVersion(),
+                ViewAndroidDelegate.createBasicDelegate(contentView),
+                contentView,
+                windowAndroid,
+                WebContents.createDefaultInternalsHolder());
+
+        mThinWebView =
+                ThinWebViewFactory.create(
+                        context,
+                        new ThinWebViewConstraints(),
+                        NullUtil.assumeNonNull(windowAndroid.getIntentRequestTracker()));
+        mThinWebView.attachWebContents(webContents, contentView, null);
+
+        mMainView = mThinWebView.getView();
+        // TODO(crbug.com/385987224): Resize according to the content size.
+        mMainView.setLayoutParams(new FrameLayout.LayoutParams(480, 480));
+
+        // This intermediate frame is needed in order for LayoutParams to take effect.
+        FrameLayout frame = new FrameLayout(context);
+        frame.addView(mMainView);
+
+        View decorView = ((Activity) anchorView.getContext()).getWindow().getDecorView();
+        mPopupWindow =
+                new AnchoredPopupWindow(
+                        context,
+                        decorView,
+                        new ColorDrawable(Color.TRANSPARENT),
+                        frame,
+                        new ViewRectProvider(anchorView));
+        mPopupWindow.setHorizontalOverlapAnchor(true);
+        mPopupWindow.setOutsideTouchable(true);
+
+        contents.setDelegate(new ContentsDelegate());
+    }
+
+    /** Cleans up resources used by this popup. */
+    @Override
+    public void destroy() {
+        mPopupWindow.dismiss();
+        mThinWebView.destroy();
+        mContents.destroy();
+    }
+
+    /** Returns the ID of the extension action this popup represents. */
+    public String getActionId() {
+        return mActionId;
+    }
+
+    /** Triggers the loading of the initial page for the extension popup. */
+    public void loadInitialPage() {
+        mContents.loadInitialPage();
+    }
+
+    /** Adds a listener that will be notified when the popup window is dismissed. */
+    public void addOnDismissListener(OnDismissListener listener) {
+        mPopupWindow.addOnDismissListener(listener);
+    }
+
+    private class ContentsDelegate implements ExtensionActionPopupContents.Delegate {
+        @Override
+        public void resizeDueToAutoResize(int width, int height) {
+            // TODO(crbug.com/385987224): Resize according to the content size.
+        }
+
+        @Override
+        public void onLoaded() {
+            mPopupWindow.show();
+        }
+    }
+}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopupContents.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopupContents.java
new file mode 100644
index 0000000..6f188ed
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopupContents.java
@@ -0,0 +1,148 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.toolbar.extensions;
+
+import org.jni_zero.CalledByNative;
+import org.jni_zero.JniType;
+import org.jni_zero.NativeMethods;
+
+import org.chromium.base.lifetime.Destroyable;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * Manages the Java-side representation of an extension action's popup.
+ *
+ * <p>An extension action popup is typically a small HTML page displayed when an extension's action
+ * button in the toolbar is clicked. This class owns the {@link WebContents} that hosts the popup's
+ * content and bridges communication with its native C++ counterpart.
+ *
+ * <p>This class implements {@link Destroyable} to ensure proper cleanup of its associated native
+ * resources. The native counterpart's lifetime is tied to this Java object; when {@link #destroy()}
+ * is called, the native object is also destroyed.
+ */
+@NullMarked
+public class ExtensionActionPopupContents implements Destroyable {
+    /**
+     * Pointer to the native C++ ExtensionActionPopupContents object. This is 0 if the native object
+     * has been destroyed.
+     */
+    private long mNativeExtensionActionPopupContents;
+
+    /** The WebContents hosting the extension popup's UI. */
+    private final WebContents mWebContents;
+
+    /** Delegate to handle UI events related to the popup, such as resizing. */
+    @Nullable private Delegate mDelegate;
+
+    @CalledByNative
+    private ExtensionActionPopupContents(
+            long nativeExtensionActionPopupContents, WebContents webContents) {
+        mNativeExtensionActionPopupContents = nativeExtensionActionPopupContents;
+        mWebContents = webContents;
+    }
+
+    /** Creates an {@link ExtensionActionPopupContents} instance. */
+    public static ExtensionActionPopupContents create(Profile profile, String actionId, int tabId) {
+        return ExtensionActionPopupContentsJni.get().create(profile, actionId, tabId);
+    }
+
+    /**
+     * Cleans up the resources associated with this popup.
+     *
+     * <p>This method must be called when the popup is no longer needed to prevent resource leaks.
+     * Safe to call multiple times; subsequent calls after the first are no-ops.
+     */
+    @Override
+    public void destroy() {
+        if (mNativeExtensionActionPopupContents == 0) {
+            return;
+        }
+        ExtensionActionPopupContentsJni.get().destroy(mNativeExtensionActionPopupContents);
+        mNativeExtensionActionPopupContents = 0;
+    }
+
+    /** Returns the {@link WebContents} that hosts the extension popup's UI. */
+    public WebContents getWebContents() {
+        return mWebContents;
+    }
+
+    /**
+     * Instructs to load the initial page for the extension popup into its {@link WebContents}.
+     *
+     * <p>This should be called after the popup is created and its view is ready to display content.
+     */
+    public void loadInitialPage() {
+        assert mNativeExtensionActionPopupContents != 0;
+        ExtensionActionPopupContentsJni.get().loadInitialPage(mNativeExtensionActionPopupContents);
+    }
+
+    /**
+     * Sets the delegate to receive UI-related callbacks for this popup.
+     *
+     * @param delegate The {@link Delegate} to handle events, or {@code null} to remove the current
+     *     delegate.
+     */
+    public void setDelegate(@Nullable Delegate delegate) {
+        mDelegate = delegate;
+    }
+
+    @CalledByNative
+    private void resizeDueToAutoResize(int width, int height) {
+        if (mDelegate != null) {
+            mDelegate.resizeDueToAutoResize(width, height);
+        }
+    }
+
+    @CalledByNative
+    private void onLoaded() {
+        if (mDelegate != null) {
+            mDelegate.onLoaded();
+        }
+    }
+
+    /**
+     * Interface for receiving UI-related callbacks from an {@link ExtensionActionPopupContents}.
+     *
+     * <p>This allows embedders or UI coordinators to react to events like content resizing.
+     */
+    public interface Delegate {
+        void resizeDueToAutoResize(int width, int height);
+
+        void onLoaded();
+    }
+
+    @NativeMethods
+    public interface Natives {
+        /**
+         * Creates the native ExtensionActionPopupContents object and returns its Java peer.
+         *
+         * @param profile The {@link Profile} associated with the extension.
+         * @param actionId The ID of the extension action.
+         * @param tabId The ID of the tab context.
+         * @return The Java {@link ExtensionActionPopupContents} object, or {@code null} on failure.
+         */
+        ExtensionActionPopupContents create(
+                @JniType("Profile*") Profile profile,
+                @JniType("std::string") String actionId,
+                int tabId);
+
+        /**
+         * Destroys the native ExtensionActionPopupContents object.
+         *
+         * @param nativeExtensionActionPopupContents The pointer to the native object.
+         */
+        void destroy(long nativeExtensionActionPopupContents);
+
+        /**
+         * Triggers the loading of the initial URL in the native ExtensionActionPopupContents.
+         *
+         * @param nativeExtensionActionPopupContents The pointer to the native object.
+         */
+        void loadInitialPage(long nativeExtensionActionPopupContents);
+    }
+}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManager.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManager.java
index 060c913..48d05a42 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManager.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManager.java
@@ -15,6 +15,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.theme.ThemeColorProvider;
+import org.chromium.ui.base.WindowAndroid;
 
 /** Provides extension-related buttons for {@link ToolbarManager}. */
 @NullMarked
@@ -23,6 +24,7 @@
     public void initialize(
             Context context,
             ViewStub extensionToolbarStub,
+            WindowAndroid windowAndroid,
             ObservableSupplier<Profile> profileSupplier,
             ObservableSupplier<Tab> currentTabSupplier,
             ThemeColorProvider themeColorProvider);
@@ -32,6 +34,7 @@
     public static ExtensionToolbarManager maybeCreate(
             Context context,
             ViewStub extensionToolbarStub,
+            WindowAndroid windowAndroid,
             ObservableSupplier<Profile> profileSupplier,
             ObservableSupplier<Tab> currentTabSupplier,
             ThemeColorProvider themeColorProvider) {
@@ -43,6 +46,7 @@
         manager.initialize(
                 context,
                 extensionToolbarStub,
+                windowAndroid,
                 profileSupplier,
                 currentTabSupplier,
                 themeColorProvider);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManagerImpl.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManagerImpl.java
index e73c004..7c9f5ca 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManagerImpl.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManagerImpl.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.theme.ThemeColorProvider;
 import org.chromium.chrome.browser.toolbar.R;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.listmenu.ListMenuButton;
 
 /**
@@ -40,6 +41,7 @@
     public void initialize(
             Context context,
             ViewStub extensionToolbarStub,
+            WindowAndroid windowAndroid,
             ObservableSupplier<Profile> profileSupplier,
             ObservableSupplier<Tab> currentTabSupplier,
             ThemeColorProvider themeColorProvider) {
@@ -48,7 +50,11 @@
         LinearLayout actionListContainer = container.findViewById(R.id.extension_action_list);
         mExtensionActionListCoordinator =
                 new ExtensionActionListCoordinator(
-                        context, actionListContainer, profileSupplier, currentTabSupplier);
+                        context,
+                        actionListContainer,
+                        windowAndroid,
+                        profileSupplier,
+                        currentTabSupplier);
 
         mExtensionsMenuButton = container.findViewById(R.id.extensions_menu_button);
         mExtensionsMenuButtonCoordinator =
diff --git a/chrome/browser/ui/ash/clipboard/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard/clipboard_history_browsertest.cc
index b2ef8a49..dccd399 100644
--- a/chrome/browser/ui/ash/clipboard/clipboard_history_browsertest.cc
+++ b/chrome/browser/ui/ash/clipboard/clipboard_history_browsertest.cc
@@ -520,15 +520,8 @@
 }
 
 // Verifies item deletion through the mouse click at the delete button.
-#if BUILDFLAG(IS_CHROMEOS)
-// TODO(https://crbug.com/394710875): Fix ui_test_utils::Navigation timeout.
-#define MAYBE_DeleteItemByClickAtDeleteButton \
-  DISABLED_DeleteItemByClickAtDeleteButton
-#else
-#define MAYBE_DeleteItemByClickAtDeleteButton DeleteItemByClickAtDeleteButton
-#endif
 IN_PROC_BROWSER_TEST_F(ClipboardHistoryBrowserTest,
-                       MAYBE_DeleteItemByClickAtDeleteButton) {
+                       DeleteItemByClickAtDeleteButton) {
   base::HistogramTester histogram_tester;
 
   // Write some things to the clipboard.
diff --git a/chrome/browser/ui/ash/input_method/candidate_window_view_pixeltest.cc b/chrome/browser/ui/ash/input_method/candidate_window_view_pixeltest.cc
index 1ce6a39..8e55db1c 100644
--- a/chrome/browser/ui/ash/input_method/candidate_window_view_pixeltest.cc
+++ b/chrome/browser/ui/ash/input_method/candidate_window_view_pixeltest.cc
@@ -5,6 +5,7 @@
 #include <optional>
 
 #include "base/i18n/base_i18n_switches.h"
+#include "base/strings/string_number_conversions.h"
 #include "chrome/browser/ui/ash/input_method/candidate_view.h"
 #include "chrome/browser/ui/ash/input_method/candidate_window_view.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
index 78d6df7..ab0e471 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
@@ -21,6 +21,7 @@
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/app/vector_icons/vector_icons.h"
diff --git a/chrome/browser/ui/ash/shelf/crostini_app_display.h b/chrome/browser/ui/ash/shelf/crostini_app_display.h
index b7792d3..44f027b19 100644
--- a/chrome/browser/ui/ash/shelf/crostini_app_display.h
+++ b/chrome/browser/ui/ash/shelf/crostini_app_display.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_ASH_SHELF_CROSTINI_APP_DISPLAY_H_
 #define CHROME_BROWSER_UI_ASH_SHELF_CROSTINI_APP_DISPLAY_H_
 
+#include <stdint.h>
+
 #include <deque>
 #include <map>
 #include <string>
diff --git a/chrome/browser/ui/bookmarks/bookmark_ui_operations_helper_unittest.cc b/chrome/browser/ui/bookmarks/bookmark_ui_operations_helper_unittest.cc
index 4e58a6c..7268333 100644
--- a/chrome/browser/ui/bookmarks/bookmark_ui_operations_helper_unittest.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_ui_operations_helper_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/notreached.h"
 #include "base/scoped_observation.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/bookmarks/bookmark_merged_surface_service.h"
 #include "chrome/browser/bookmarks/bookmark_merged_surface_service_factory.h"
diff --git a/chrome/browser/ui/download/download_bubble_security_view_info_unittest.cc b/chrome/browser/ui/download/download_bubble_security_view_info_unittest.cc
index bc956e2..545274c 100644
--- a/chrome/browser/ui/download/download_bubble_security_view_info_unittest.cc
+++ b/chrome/browser/ui/download/download_bubble_security_view_info_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/download/download_bubble_security_view_info.h"
 
 #include "base/strings/pattern.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/download/bubble/download_bubble_prefs.h"
 #include "chrome/browser/download/download_item_model.h"
diff --git a/chrome/browser/ui/passwords/bubble_controllers/manage_passwords_bubble_controller_unittest.cc b/chrome/browser/ui/passwords/bubble_controllers/manage_passwords_bubble_controller_unittest.cc
index fd735d4..3dfacea 100644
--- a/chrome/browser/ui/passwords/bubble_controllers/manage_passwords_bubble_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/bubble_controllers/manage_passwords_bubble_controller_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
diff --git a/chrome/browser/ui/tabs/tab_renderer_data.cc b/chrome/browser/ui/tabs/tab_renderer_data.cc
index 5a1673c..b181e1b0 100644
--- a/chrome/browser/ui/tabs/tab_renderer_data.cc
+++ b/chrome/browser/ui/tabs/tab_renderer_data.cc
@@ -126,7 +126,7 @@
       data.pinned || model->delegate()->ShouldDisplayFavicon(contents);
   data.blocked = model->IsTabBlocked(index);
   data.should_hide_throbber = tab_ui_helper->ShouldHideThrobber();
-  data.alert_state = GetTabAlertStatesForContents(contents);
+  data.alert_state = GetTabAlertStatesForTab(tab);
 
   content::NavigationEntry* entry =
       contents->GetController().GetLastCommittedEntry();
diff --git a/chrome/browser/ui/tabs/tab_strip_api/converters/tab_converters.cc b/chrome/browser/ui/tabs/tab_strip_api/converters/tab_converters.cc
index 2d70e6f..134d812b 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/converters/tab_converters.cc
+++ b/chrome/browser/ui/tabs/tab_strip_api/converters/tab_converters.cc
@@ -22,8 +22,7 @@
   result->url = data.visible_url;
   result->network_state = data.network_state;
   if (handle.Get() != nullptr) {
-    for (const auto alert_state :
-         GetTabAlertStatesForContents(handle.Get()->GetContents())) {
+    for (const auto alert_state : GetTabAlertStatesForTab(handle.Get())) {
       result->alert_states.push_back(alert_state);
     }
   }
diff --git a/chrome/browser/ui/tabs/tab_utils.cc b/chrome/browser/ui/tabs/tab_utils.cc
index 16d9767..6097701 100644
--- a/chrome/browser/ui/tabs/tab_utils.cc
+++ b/chrome/browser/ui/tabs/tab_utils.cc
@@ -30,9 +30,13 @@
 #include "chrome/browser/glic/resources/grit/glic_browser_resources.h"
 #endif
 
-std::vector<tabs::TabAlert> GetTabAlertStatesForContents(
-    content::WebContents* contents) {
+std::vector<tabs::TabAlert> GetTabAlertStatesForTab(
+    const tabs::TabInterface* tab) {
   std::vector<tabs::TabAlert> states;
+  if (!tab) {
+    return states;
+  }
+  content::WebContents* contents = tab->GetContents();
   if (!contents) {
     return states;
   }
diff --git a/chrome/browser/ui/tabs/tab_utils.h b/chrome/browser/ui/tabs/tab_utils.h
index c5aa63f..4984daa 100644
--- a/chrome/browser/ui/tabs/tab_utils.h
+++ b/chrome/browser/ui/tabs/tab_utils.h
@@ -21,6 +21,7 @@
 
 namespace tabs {
 enum class TabAlert;
+class TabInterface;
 }  // namespace tabs
 
 struct LastMuteMetadata
@@ -39,8 +40,8 @@
 // privacy, i.e. if only one is to be shown, it should be the first.
 // TabAlertState::NONE will never be present in the list; an empty list
 // is returned instead.
-std::vector<tabs::TabAlert> GetTabAlertStatesForContents(
-    content::WebContents* contents);
+std::vector<tabs::TabAlert> GetTabAlertStatesForTab(
+    const tabs::TabInterface* tab);
 
 // Returns a localized string describing the |alert_state|.
 std::u16string GetTabAlertStateText(const tabs::TabAlert alert_state);
diff --git a/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view.cc b/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view.cc
index 1317b16..60a9f03 100644
--- a/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_iban_bubble_view.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/autofill/payments/save_iban_bubble_view.h"
 
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/autofill/payments/save_iban_ui.h"
 #include "chrome/browser/ui/views/accessibility/theme_tracking_non_accessible_image_view.h"
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
index 7879b94..6351999 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
@@ -12,6 +12,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
 #include "chrome/browser/extensions/extension_install_prompt_show_params.h"
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index dd3a0b0..19b7b3b8 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -435,22 +435,6 @@
 
 #endif  // DCHECK_IS_ON()
 
-void MaybeResetStoredFocusForWebContents(content::WebContents* web_contents) {
-  // In the case that the last focused view of the WebContents is a
-  // ContentsWebView, but not the ContentsWebView hosting the WebContents
-  // itself, we must reset the stored focus to prevent incorrect split-tab
-  // activation behavior when the split-view is swapped in during a tab switch.
-  ChromeWebContentsViewFocusHelper* focus_helper =
-      ChromeWebContentsViewFocusHelper::FromWebContents(web_contents);
-  if (focus_helper) {
-    ContentsWebView* focused_view =
-        views::AsViewClass<ContentsWebView>(focus_helper->GetStoredFocus());
-    if (focused_view && focused_view->web_contents() != web_contents) {
-      focus_helper->ResetStoredFocus();
-    }
-  }
-}
-
 bool GetGestureCommand(ui::GestureEvent* event, int* command) {
   DCHECK(command);
   *command = 0;
@@ -1667,6 +1651,25 @@
   }
 }
 
+void BrowserView::MaybeUpdateStoredFocusForWebContents(
+    content::WebContents* web_contents) {
+  ChromeWebContentsViewFocusHelper* focus_helper =
+      ChromeWebContentsViewFocusHelper::FromWebContents(web_contents);
+  if (!focus_helper) {
+    return;
+  }
+
+  // In the case that the last focused view of the WebContents is a
+  // ContentsWebView, but not the ContentsWebView hosting the WebContents
+  // itself, we must reset the stored focus to prevent incorrect tab
+  // activation behavior when the split view is swapped in during a tab switch.
+  ContentsWebView* focused_view =
+      views::AsViewClass<ContentsWebView>(focus_helper->GetStoredFocus());
+  if (focused_view && focused_view->web_contents() != web_contents) {
+    focus_helper->SetStoredFocusView(GetContentsView());
+  }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // BrowserView, BrowserWindow implementation:
 
@@ -2196,7 +2199,6 @@
         multi_contents_view_->GetActiveContentsView()->SetWebContents(
             new_contents);
       }
-      MaybeResetStoredFocusForWebContents(new_contents);
     } else {
       active_contents_view->SetWebContents(new_contents);
     }
@@ -2213,10 +2215,17 @@
     UpdateActiveTabInSplitView();
   }
 
+  MaybeUpdateStoredFocusForWebContents(new_contents);
+
   if (will_restore_focus) {
     // We only restore focus if our window is visible, to avoid invoking blur
     // handlers when we are eventually shown.
     new_contents->RestoreFocus();
+  } else if (!GetWidget()->IsActive()) {
+    // When the window is inactive during tab switch, restore focus for the
+    // active web content on activation.
+    GetFocusManager()->SetStoredFocusView(nullptr);
+    restore_focus_on_activation_ = true;
   }
 
   // Update all the UI bits.
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 339a9737..26c90a4 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -516,6 +516,9 @@
   // Activate the tab containing the given WebContents (if any).
   void ActivateWebContents(content::WebContents* web_contents);
 
+  // Updates stored focus for web contents that is being activated.
+  void MaybeUpdateStoredFocusForWebContents(content::WebContents*);
+
   // BrowserWindow:
   void Show() override;
   void ShowInactive() override;
diff --git a/chrome/browser/ui/views/frame/browser_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_view_browsertest.cc
index b6adbee..8f7d65f 100644
--- a/chrome/browser/ui/views/frame/browser_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_browsertest.cc
@@ -207,9 +207,15 @@
 #endif
 
 // Verifies that page and devtools WebViews are being correctly laid out
-// when DevTools is opened/closed/updated/undocked.
-// TODO(crbug.com/40834238): Re-enable; currently failing on multiple platforms.
-IN_PROC_BROWSER_TEST_F(BrowserViewTest, DISABLED_DevToolsUpdatesBrowserWindow) {
+// when DevTools is opened/closed/updated while docked.
+IN_PROC_BROWSER_TEST_F(BrowserViewTest, DevToolsDockedUpdatesBrowserWindow) {
+#if BUILDFLAG(IS_OZONE)
+  // Ozone/wayland doesn't support getting/setting window position in global
+  // screen coordinates. So this test is not applicable.
+  if (ui::OzonePlatform::GetPlatformNameForTest() == "wayland") {
+    GTEST_SKIP();
+  }
+#endif
   gfx::Rect full_bounds =
       browser_view()->GetContentsContainerForTest()->GetLocalBounds();
   gfx::Rect small_bounds(10, 20, 30, 40);
@@ -243,8 +249,22 @@
   EXPECT_FALSE(devtools_web_view()->web_contents());
   EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
   EXPECT_EQ(full_bounds, contents_web_view()->bounds());
+}
 
-  // Undocked.
+// Verifies that page and devtools WebViews are being correctly laid out
+// when DevTools is opened/closed/updated while undocked.
+IN_PROC_BROWSER_TEST_F(BrowserViewTest, DevToolsUndockedUpdatesBrowserWindow) {
+#if BUILDFLAG(IS_OZONE)
+  // Ozone/wayland doesn't support getting/setting window position in global
+  // screen coordinates. So this test is not applicable.
+  if (ui::OzonePlatform::GetPlatformNameForTest() == "wayland") {
+    GTEST_SKIP();
+  }
+#endif
+  gfx::Rect full_bounds =
+      browser_view()->GetContentsContainerForTest()->GetLocalBounds();
+  gfx::Rect small_bounds(10, 20, 30, 40);
+
   OpenDevToolsWindow(false);
   EXPECT_TRUE(devtools_web_view()->web_contents());
   EXPECT_EQ(full_bounds, devtools_web_view()->bounds());
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
index 1b9a634..94a6116 100644
--- a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
@@ -185,7 +185,7 @@
 
 // Tests switching tabs with split views. This also adds coverage to ensuring
 // that there isn't any unnecessary re-layout during tab switching.
-IN_PROC_BROWSER_TEST_F(MultiContentsViewUiTest, TabSwitchWithSplitViews) {
+IN_PROC_BROWSER_TEST_F(MultiContentsViewUiTest, TabSwitchWithSplitView) {
   DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(MultiContentsViewBoundsChangedObserver,
                                       kMultiContentsViewBoundsChangedObserver);
   RunTestSequence(
@@ -237,6 +237,70 @@
       EnterSplitView(2, 0), CheckTabIsActive(2), CheckActiveContentsHasFocus());
 }
 
+// Split view active tab change while browser window doesn't have focus. This
+// is used to simulate tab switching scenarios using Tab Search
+IN_PROC_BROWSER_TEST_F(MultiContentsViewUiTest,
+                       TabChangeInSplitViewWithInactiveBrowserWindow) {
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFirstTab);
+
+  RunTestSequence(
+      InstrumentTab(kFirstTab, 0),
+      NavigateWebContents(kFirstTab, GURL(chrome::kChromeUISettingsURL)),
+      FocusWebContents(kFirstTab),
+      AddInstrumentedTab(kNewTab, GURL(chrome::kChromeUISettingsURL), 1),
+      FocusWebContents(kNewTab),
+      AddInstrumentedTab(kSecondTab, GURL(chrome::kChromeUISettingsURL), 2),
+      FocusWebContents(kSecondTab),
+      CheckResult([this]() { return tab_strip_model()->count(); }, 3u),
+      EnterSplitView(2, 0), CheckTabIsActive(2),
+      PressButton(kTabSearchButtonElementId),
+      WaitForShow(kTabSearchBubbleElementId),
+      Do([this]() { browser()->tab_strip_model()->ActivateTabAt(1); }),
+      WaitForHide(kTabSearchBubbleElementId), CheckTabIsActive(1),
+      CheckActiveContentsHasFocus());
+}
+
+// Switch to the not last used tab inside a split view from a not split tab
+// while the browser is inactive. This is used to simulate tab switching
+// scenarios using Tab Search
+IN_PROC_BROWSER_TEST_F(MultiContentsViewUiTest,
+                       SwitchToSplitViewWithInactiveBrowserWindow) {
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFirstTab);
+
+  RunTestSequence(
+      InstrumentTab(kFirstTab, 0),
+      NavigateWebContents(kFirstTab, GURL(chrome::kChromeUISettingsURL)),
+      FocusWebContents(kFirstTab),
+      AddInstrumentedTab(kNewTab, GURL(chrome::kChromeUISettingsURL), 1),
+      FocusWebContents(kNewTab),
+      AddInstrumentedTab(kSecondTab, GURL(chrome::kChromeUISettingsURL), 2),
+      FocusWebContents(kSecondTab),
+      CheckResult([this]() { return tab_strip_model()->count(); }, 3u),
+      EnterSplitView(2, 0), CheckTabIsActive(2),
+      // Switch from the split view to a regular tab
+      SelectTab(kTabStripElementId, 0, InputType::kMouse), CheckTabIsActive(0),
+      FocusWebContents(kNewTab),
+      // Launch the tab search bubble using the tab search button
+      PressButton(kTabSearchButtonElementId),
+      WaitForShow(kTabSearchBubbleElementId),
+      // Switch from a regular tab directly to an inactive tab, which is on
+      // the left side of a split with the TabSearch bubble dialog opened.
+      Do([this]() { browser()->tab_strip_model()->ActivateTabAt(1); }),
+      WaitForHide(kTabSearchBubbleElementId), CheckTabIsActive(1),
+      CheckActiveContentsHasFocus(),
+      // Switch out of the split view back to the regular tab
+      SelectTab(kTabStripElementId, 0, InputType::kMouse), CheckTabIsActive(0),
+      FocusWebContents(kNewTab),
+      // Launch the tab search bubble using the tab search button
+      PressButton(kTabSearchButtonElementId),
+      WaitForShow(kTabSearchBubbleElementId),
+      // Switch from a regular tab directly to an inactive tab, which is on
+      // the right side of a split with the TabSearch bubble dialog opened.
+      Do([this]() { browser()->tab_strip_model()->ActivateTabAt(2); }),
+      WaitForHide(kTabSearchBubbleElementId), CheckTabIsActive(2),
+      CheckActiveContentsHasFocus());
+}
+
 IN_PROC_BROWSER_TEST_F(MultiContentsViewUiTest, ResizesToMinWidth) {
   RunTestSequence(
       CreateTabsAndEnterSplitView(), ResizeWindow(1000),
diff --git a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
index 7f5daf3..11731b8f 100644
--- a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
@@ -891,42 +891,14 @@
 }
 
 void PictureInPictureBrowserFrameView::SetFrameBounds(const gfx::Rect& bounds) {
-  gfx::Rect adjusted_bounds(bounds);
-  gfx::Rect current_bounds = GetWidget()->GetWindowBoundsInScreen();
-  bool did_adjust_size = false;
-
-  // If the website is requesting that the window increases in size, then ensure
-  // that it's not increasing beyond the site-requested maximum.
-  if (bounds.size().width() > current_bounds.size().width() ||
-      bounds.size().height() > current_bounds.size().height()) {
-    auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(
-        GetWidget()->GetNativeWindow());
-    gfx::Size adjusted_new_size =
-        PictureInPictureWindowManager::AdjustRequestedSizeIfNecessary(
-            bounds.size(), display);
-
-    // If so, then use the adjusted size centered on the current location rather
-    // than centered on the new location (as we only ever expect size to change,
-    // and a large requested size could incidentally move the window).
-    if (adjusted_new_size != bounds.size()) {
-      adjusted_bounds = current_bounds;
-      adjusted_bounds.ClampToCenteredSize(adjusted_new_size);
-      did_adjust_size = true;
-    }
-  }
-
-  base::UmaHistogramBoolean(
-      "Media.DocumentPictureInPicture.RequestedLargeResize", did_adjust_size);
-
   if (!base::FeatureList::IsEnabled(
           media::kDocumentPictureInPictureAnimateResize) ||
       !gfx::Animation::ShouldRenderRichAnimation()) {
-    BrowserNonClientFrameView::SetFrameBounds(adjusted_bounds);
+    BrowserNonClientFrameView::SetFrameBounds(bounds);
     return;
   }
   bounds_change_animation_ =
-      std::make_unique<BrowserFrameBoundsChangeAnimation>(*frame(),
-                                                          adjusted_bounds);
+      std::make_unique<BrowserFrameBoundsChangeAnimation>(*frame(), bounds);
   bounds_change_animation_->Start();
 }
 
diff --git a/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.cc b/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.cc
index a7c72c8..afed2899 100644
--- a/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.cc
+++ b/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.cc
@@ -171,6 +171,12 @@
       lens::LensOverlayInvocationSource::kHomeworkActionChip);
   UserEducationService::MaybeNotifyNewBadgeFeatureUsed(
       GetWebContents()->GetBrowserContext(), lens::features::kLensOverlay);
+
+  // TODO(crbug.com/422844464): Fix Update() so that it correctly handles the
+  // chip disappearing at this point, so that the following lines are no longer
+  // needed.
+  SetVisible(false);
+  ResetSlideAnimation(true);
 }
 
 views::BubbleDialogDelegate* LensOverlayHomeworkPageActionIconView::GetBubble()
diff --git a/chrome/browser/ui/views/omnibox/omnibox_row_view.cc b/chrome/browser/ui/views/omnibox/omnibox_row_view.cc
index bc4cdfd0..7727980 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_row_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_row_view.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/omnibox/omnibox_row_view.h"
 
+#include "base/strings/string_number_conversions.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/omnibox/omnibox_theme.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_header_view.h"
diff --git a/chrome/browser/ui/views/page_info/page_info_permission_content_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_permission_content_view_unittest.cc
index 9eda14b..45c208c 100644
--- a/chrome/browser/ui/views/page_info/page_info_permission_content_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_permission_content_view_unittest.cc
@@ -7,6 +7,7 @@
 #if !BUILDFLAG(IS_CHROMEOS)
 
 #include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_details_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_details_view.cc
index 9fe07b8..a4282bd 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_details_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_details_view.cc
@@ -8,6 +8,7 @@
 
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ui/layout_constants.h"
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble_one_origin_view_unittest.cc b/chrome/browser/ui/views/permissions/permission_prompt_bubble_one_origin_view_unittest.cc
index f9f00b2..479f76a 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble_one_origin_view_unittest.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble_one_origin_view_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/containers/to_vector.h"
 #include "base/memory/raw_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.h"
diff --git a/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc b/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc
index 24fa45b..0883205 100644
--- a/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc
+++ b/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
diff --git a/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.cc b/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.cc
index f075b14..f70fa9ff 100644
--- a/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.cc
+++ b/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.cc
@@ -80,6 +80,10 @@
   return nullptr;
 }
 
+void ChromeWebContentsViewFocusHelper::SetStoredFocusView(views::View* view) {
+  last_focused_view_tracker_.SetView(view);
+}
+
 gfx::NativeView ChromeWebContentsViewFocusHelper::GetActiveNativeView() {
   return GetWebContents().GetNativeView();
 }
diff --git a/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.h b/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.h
index 30853310..41e643b 100644
--- a/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.h
+++ b/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.h
@@ -35,6 +35,7 @@
   bool TakeFocus(bool reverse);
   // Returns the View that will be focused if RestoreFocus() is called.
   views::View* GetStoredFocus();
+  void SetStoredFocusView(views::View* view);
 
  private:
   explicit ChromeWebContentsViewFocusHelper(content::WebContents* web_contents);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_controller_unittest.cc b/chrome/browser/ui/views/toolbar/toolbar_controller_unittest.cc
index a683707..fe3b62941 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_controller_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_controller_unittest.cc
@@ -10,6 +10,7 @@
 #include <variant>
 
 #include "base/memory/raw_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "chrome/browser/ui/toolbar/pinned_toolbar/pinned_toolbar_actions_model.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/toolbar/overflow_button.h"
diff --git a/chrome/browser/ui/web_applications/app_browser_document_picture_in_picture_browsertest.cc b/chrome/browser/ui/web_applications/app_browser_document_picture_in_picture_browsertest.cc
index 2f5bd97a..cd8b854 100644
--- a/chrome/browser/ui/web_applications/app_browser_document_picture_in_picture_browsertest.cc
+++ b/chrome/browser/ui/web_applications/app_browser_document_picture_in_picture_browsertest.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/functional/bind.h"
-#include "base/functional/callback.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/picture_in_picture/document_picture_in_picture_mixin_test_base.h"
 #include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
@@ -23,15 +21,11 @@
 namespace {
 
 // Helper class to wait for widget bound changes. Stops waiting once widget size
-// passes the given `size_ready_callback` check.
+// matches the `expected_size_`.
 class WidgetResizeWaiter : public views::WidgetObserver {
  public:
-  // Should return true if the given size should end waiting.
-  using SizeReadyCallback = base::RepeatingCallback<bool(const gfx::Size&)>;
-
-  WidgetResizeWaiter(views::Widget* widget,
-                     SizeReadyCallback size_ready_callback)
-      : size_ready_callback_(std::move(size_ready_callback)) {
+  explicit WidgetResizeWaiter(views::Widget* widget, gfx::Size expected_size)
+      : expected_size_(expected_size) {
     observation_.Observe(widget);
   }
 
@@ -39,7 +33,7 @@
 
   void OnWidgetBoundsChanged(views::Widget* widget,
                              const gfx::Rect& bounds) override {
-    if (size_ready_callback_.Run(bounds.size())) {
+    if (bounds.size() == expected_size_) {
       run_loop_.Quit();
     }
   }
@@ -47,7 +41,7 @@
  private:
   base::ScopedObservation<views::Widget, views::WidgetObserver> observation_{
       this};
-  SizeReadyCallback size_ready_callback_;
+  gfx::Size expected_size_;
   base::RunLoop run_loop_;
 };
 
@@ -219,20 +213,12 @@
   {
     WidgetResizeWaiter waiter(
         pip_browser_view->GetWidget(),
-        base::BindRepeating(
-            [](gfx::Size maximum_window_size, const gfx::Size& size) {
-              return size.width() <= maximum_window_size.width() &&
-                     size.height() <= maximum_window_size.height();
-            },
-            maximum_window_size));
+        PictureInPictureWindowManager::GetMaximumWindowSize(display));
     EXPECT_TRUE(ExecJs(pip_web_contents, script));
     waiter.Wait();
   }
 
-  EXPECT_LE(pip_browser_view->GetBounds().size().width(),
-            maximum_window_size.width());
-  EXPECT_LE(pip_browser_view->GetBounds().size().height(),
-            maximum_window_size.height());
+  EXPECT_EQ(pip_browser_view->GetBounds().size(), maximum_window_size);
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
index 5512da5..6961c90 100644
--- a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
+++ b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/password_manager/password_manager_ui.h"
 
 #include "base/i18n/message_formatter.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h"
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
index 9b7bad2..deef4f9 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc
@@ -1447,8 +1447,7 @@
           ? custom_last_active_text
           : GetLastActiveElapsedText(last_active_time_ticks);
 
-  std::vector<tabs::TabAlert> alert_states =
-      GetTabAlertStatesForContents(contents);
+  std::vector<tabs::TabAlert> alert_states = GetTabAlertStatesForTab(tab);
   // Currently, we only report media alert states.
   std::ranges::copy_if(alert_states.begin(), alert_states.end(),
                        std::back_inserter(tab_data->alert_states),
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
index 7fb12363..ac6b209 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
@@ -307,7 +307,8 @@
   switch (change.type()) {
     case TabStripModelChange::kInserted: {
       for (const auto& contents : change.GetInsert()->contents) {
-        page_->TabCreated(GetTabData(contents.contents, contents.index));
+        page_->TabCreated(
+            GetTabData(contents.contents, contents.tab, contents.index));
       }
       break;
     }
@@ -350,18 +351,23 @@
                                        int index,
                                        TabChangeType change_type) {
   TRACE_EVENT0("browser", "TabStripPageHandler:TabChangedAt");
-  page_->TabUpdated(GetTabData(contents, index));
+  TabStripModel* tab_strip_model = browser_->tab_strip_model();
+  page_->TabUpdated(
+      GetTabData(contents, tab_strip_model->GetTabAtIndex(index), index));
 }
 
 void TabStripPageHandler::TabPinnedStateChanged(TabStripModel* tab_strip_model,
                                                 content::WebContents* contents,
                                                 int index) {
-  page_->TabUpdated(GetTabData(contents, index));
+  page_->TabUpdated(
+      GetTabData(contents, tab_strip_model->GetTabAtIndex(index), index));
 }
 
 void TabStripPageHandler::TabBlockedStateChanged(content::WebContents* contents,
                                                  int index) {
-  page_->TabUpdated(GetTabData(contents, index));
+  TabStripModel* tab_strip_model = browser_->tab_strip_model();
+  page_->TabUpdated(
+      GetTabData(contents, tab_strip_model->GetTabAtIndex(index), index));
 }
 
 bool TabStripPageHandler::PreHandleGestureEvent(
@@ -490,6 +496,7 @@
 
 tab_strip::mojom::TabPtr TabStripPageHandler::GetTabData(
     content::WebContents* contents,
+    const tabs::TabInterface* tab,
     int index) {
   DCHECK(index >= 0);
   auto tab_data = tab_strip::mojom::Tab::New();
@@ -544,7 +551,7 @@
   tab_data->crashed = tab_renderer_data.IsCrashed();
   // TODO(johntlee): Add the rest of TabRendererData
 
-  for (const auto alert_state : GetTabAlertStatesForContents(contents)) {
+  for (const auto alert_state : GetTabAlertStatesForTab(tab)) {
     tab_data->alert_states.push_back(alert_state);
   }
 
@@ -576,7 +583,8 @@
   std::vector<tab_strip::mojom::TabPtr> tabs;
   TabStripModel* tab_strip_model = browser_->tab_strip_model();
   for (int i = 0; i < tab_strip_model->count(); ++i) {
-    tabs.push_back(GetTabData(tab_strip_model->GetWebContentsAt(i), i));
+    tabs.push_back(GetTabData(tab_strip_model->GetWebContentsAt(i),
+                              tab_strip_model->GetTabAtIndex(i), i));
   }
   std::move(callback).Run(std::move(tabs));
 }
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
index 99be7c9..c4836cd 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
@@ -27,6 +27,10 @@
 class Browser;
 class TabStripUIEmbedder;
 
+namespace tabs {
+class TabInterface;
+}  // namespace tabs
+
 class TabStripPageHandler : public tab_strip::mojom::PageHandler,
                             public TabStripModelObserver,
                             public content::WebContentsDelegate,
@@ -97,6 +101,7 @@
 
   void OnLongPressTimer();
   tab_strip::mojom::TabPtr GetTabData(content::WebContents* contents,
+                                      const tabs::TabInterface* tab,
                                       int index);
   tab_strip::mojom::TabGroupVisualDataPtr GetTabGroupData(TabGroup* group);
   void HandleThumbnailUpdate(content::WebContents* tab,
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler_unittest.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler_unittest.cc
index ef96894..d06a6138 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <optional>
 
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/values.h"
diff --git a/chrome/browser/ui/webui/tabs/OWNERS b/chrome/browser/ui/webui/tabs/OWNERS
index 6701594..16fe190 100644
--- a/chrome/browser/ui/webui/tabs/OWNERS
+++ b/chrome/browser/ui/webui/tabs/OWNERS
@@ -1,7 +1,7 @@
-elainechien@chromium.org
 romanarora@chromium.org
+yuhengh@chromium.org
 
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
 per-file *_mojom_traits*.*=set noparent
-per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
\ No newline at end of file
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/chrome/browser/web_applications/extensions/extensions_manager.cc b/chrome/browser/web_applications/extensions/extensions_manager.cc
index 5746718..b71545f 100644
--- a/chrome/browser/web_applications/extensions/extensions_manager.cc
+++ b/chrome/browser/web_applications/extensions/extensions_manager.cc
@@ -74,27 +74,6 @@
   return allowlist;
 }
 
-bool ExtensionsManager::ShouldGarbageCollectStoragePartitions() {
-  // `ExtensionPrefs` can be created lazily, so we don't need to wait on
-  // extension service.
-  extensions::ExtensionPrefs* extension_prefs =
-      extensions::ExtensionPrefs::Get(profile_);
-  return extension_prefs && extension_prefs->NeedsStorageGarbageCollection();
-}
-
-void ExtensionsManager::ResetStorageGarbageCollectPref(
-    base::OnceClosure callback) {
-  // `ExtensionPrefs` can be created lazily, so we don't need to wait on
-  // extension service.
-  extensions::ExtensionPrefs* extension_prefs =
-      extensions::ExtensionPrefs::Get(profile_);
-  if (extension_prefs) {
-    extension_prefs->pref_service()->SetBoolean(
-        extensions::pref_names::kStorageGarbageCollect, false);
-    extension_prefs->pref_service()->CommitPendingWrite(std::move(callback));
-  }
-}
-
 std::unique_ptr<ExtensionInstallGate>
 ExtensionsManager::RegisterGarbageCollectionInstallGate() {
   return std::make_unique<web_app::ExtensionInstallGateImpl>(profile_);
diff --git a/chrome/browser/web_applications/extensions_manager.h b/chrome/browser/web_applications/extensions_manager.h
index d1f305b..58c42ff 100644
--- a/chrome/browser/web_applications/extensions_manager.h
+++ b/chrome/browser/web_applications/extensions_manager.h
@@ -38,14 +38,6 @@
 
   virtual std::unordered_set<base::FilePath> GetIsolatedStoragePaths();
 
-  // Returns ExtensionsPref::kStorageGarbageCollect which indicates possibly
-  // deleted Storage Partitions on disk requiring garbage collection.
-  // TODO(crbug.com/40922689): Delete ExtensionsPref::kStorageGarbageCollect.
-  virtual bool ShouldGarbageCollectStoragePartitions();
-
-  // Sets ExtensionsPref::kStorageGarbageCollect to false.
-  virtual void ResetStorageGarbageCollectPref(base::OnceClosure callback);
-
   // Creates an ExtensionInstallerGate which registers itself on
   // ExtensionService to delay Extension installs.
   virtual std::unique_ptr<ExtensionInstallGate>
diff --git a/chrome/browser/web_applications/isolated_web_apps/commands/garbage_collect_storage_partitions_command.cc b/chrome/browser/web_applications/isolated_web_apps/commands/garbage_collect_storage_partitions_command.cc
index 8111deb..47cc0b10 100644
--- a/chrome/browser/web_applications/isolated_web_apps/commands/garbage_collect_storage_partitions_command.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/commands/garbage_collect_storage_partitions_command.cc
@@ -54,10 +54,6 @@
   // resetting to false early.
   profile_->GetPrefs()->SetBoolean(
       prefs::kShouldGarbageCollectStoragePartitions, false);
-  // Waits for both prefs to be written to disk before proceeding to prevent
-  // repeating crashes.
-  lock_->extensions_manager().ResetStorageGarbageCollectPref(
-      concurrent.CreateClosure());
   profile_->GetPrefs()->CommitPendingWrite(concurrent.CreateClosure());
   std::move(concurrent)
       .Done(base::BindOnce(&GarbageCollectStoragePartitionsCommand::OnPrefReset,
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_installation_manager.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_installation_manager.cc
index c34ac13..0cbf1fc1 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_installation_manager.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_installation_manager.cc
@@ -466,13 +466,8 @@
 }
 
 void IsolatedWebAppInstallationManager::MaybeScheduleGarbageCollection() {
-  // We are migrating from `ExtensionsPref::kStorageGarbageCollect` to
-  // `prefs::kShouldGarbageCollectStoragePartitions`. During the migration,
-  // either one of the prefs can trigger garbage collection.
-  // TODO(crbug.com/40922689): Delete `ExtensionsPref::kStorageGarbageCollect`.
   if (profile_->GetPrefs()->GetBoolean(
-          prefs::kShouldGarbageCollectStoragePartitions) ||
-      provider_->extensions_manager().ShouldGarbageCollectStoragePartitions()) {
+          prefs::kShouldGarbageCollectStoragePartitions)) {
     provider_->command_manager().ScheduleCommand(
         std::make_unique<web_app::GarbageCollectStoragePartitionsCommand>(
             &profile_.get(),
diff --git a/chrome/browser/web_applications/isolated_web_apps/key_distribution/iwa_key_distribution_info_provider_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/key_distribution/iwa_key_distribution_info_provider_unittest.cc
index e704a22..13ae8d7 100644
--- a/chrome/browser/web_applications/isolated_web_apps/key_distribution/iwa_key_distribution_info_provider_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/key_distribution/iwa_key_distribution_info_provider_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/scoped_path_override.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/version.h"
 #include "chrome/browser/component_updater/iwa_key_distribution_component_installer.h"
 #include "chrome/browser/web_applications/isolated_web_apps/iwa_identity_validator.h"
diff --git a/chrome/browser/web_applications/locks/web_app_lock_manager.cc b/chrome/browser/web_applications/locks/web_app_lock_manager.cc
index 1869f86..44e3878 100644
--- a/chrome/browser/web_applications/locks/web_app_lock_manager.cc
+++ b/chrome/browser/web_applications/locks/web_app_lock_manager.cc
@@ -15,6 +15,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/to_string.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/task_runner.h"
 #include "base/types/pass_key.h"
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
index 019ac7e..6e6ba54 100644
--- a/chrome/browser/win/chrome_process_finder.cc
+++ b/chrome/browser/win/chrome_process_finder.cc
@@ -20,7 +20,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/win/message_window.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/win_util.h"
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 37790ff..b543a9e 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1749153838-ceba28aa4231d55de2d5c3044b579bea55cc2f2f-d0051c9105d286b12ca0e06027eee86eef8d23e3.profdata
+chrome-android64-main-1749167123-a0ad2132230a4e0439f87fdd8a87e34cfe88172d-b795f62e31ff577fa4b982385931634663b70e63.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 2b7c26d..defcc35 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1749146264-08be9ef2c8d32a7e2f0b5e539442a8e1ebf9c428-46225ed4889da320c0d607a3a8e9ce0400bd3e88.profdata
+chrome-linux-main-1749167123-293fd6638f0497c300313ab3e8e310974cf00832-b795f62e31ff577fa4b982385931634663b70e63.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 512be71..a9fcbe4 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1749160339-e3b5e65b915732db0524b523a1e19a5c14855359-426353f6aa9c4aa0ba2aa86037878fd70cc31978.profdata
+chrome-mac-arm-main-1749189211-e4c66fdb1539547ad8f307f14b5e1dd450462cc0-e074fdf9bf476ecb65885b2fc4b04be6d99b5ab3.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 6929db59..e35c56b 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1749146264-c2239ee6ba19dcec39e96903fefd2269cb9e695c-46225ed4889da320c0d607a3a8e9ce0400bd3e88.profdata
+chrome-mac-main-1749167123-8c1216a847ba10d1ec64777522a5913741e795d2-b795f62e31ff577fa4b982385931634663b70e63.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 859bb5a..b359a154 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1749135429-5b56efea6604cfbe7785014fa474c2d4fe7d82a7-2ef25ae91c6fed5f715c962d43ca9cceb5da14e4.profdata
+chrome-win32-main-1749167123-67bbb7f7d4be123087527492e3014c469885ca3a-b795f62e31ff577fa4b982385931634663b70e63.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 470fb43a..6783e67 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1749135429-5f146d27ad510f64753a2aa20c28c5f2a6492dee-2ef25ae91c6fed5f715c962d43ca9cceb5da14e4.profdata
+chrome-win64-main-1749157182-a9e9c1a2e1d9e6a37d825cf4c3791d906170cc35-d641dd235e14d971eec18239f5e90d33f985800b.profdata
diff --git a/chrome/common/actor.mojom b/chrome/common/actor.mojom
index 7088375..8aca4ea8 100644
--- a/chrome/common/actor.mojom
+++ b/chrome/common/actor.mojom
@@ -179,6 +179,10 @@
   // The task for the action was paused.
   kTaskPaused = 23,
 
+  // The tool executor in the renderer was destroyed before the tool finished
+  // executing.
+  kExecutorDestroyed = 24,
+
   ///////////////////////////////////////////////////////////////////////
   // Codes 100-199: Errors for navigation. (Not part of the ToolAction union
   // as it's a browser-side tool.)
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc
index e9d3d68..49a7a79 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc
@@ -13,6 +13,7 @@
 #include "base/containers/span.h"
 #include "base/memory/raw_ptr.h"
 #include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/types/cxx23_to_underlying.h"
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_test_utils.cc b/chrome/renderer/accessibility/read_anything/read_anything_test_utils.cc
index baf2a46..68fc49d 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_test_utils.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_test_utils.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/renderer/accessibility/read_anything/read_anything_test_utils.h"
 
+#include "base/strings/string_number_conversions.h"
+
 namespace test {
 
 void SetUpdateTreeID(ui::AXTreeUpdate* update, ui::AXTreeID tree_id) {
diff --git a/chrome/renderer/actor/click_tool.cc b/chrome/renderer/actor/click_tool.cc
index b6cacd2..0ab59877 100644
--- a/chrome/renderer/actor/click_tool.cc
+++ b/chrome/renderer/actor/click_tool.cc
@@ -45,11 +45,10 @@
 
 ClickTool::~ClickTool() = default;
 
-void ClickTool::Execute(ToolFinishedCallback callback) {
+mojom::ActionResultPtr ClickTool::Execute() {
   ValidatedResult validated_result = Validate();
   if (!validated_result.has_value()) {
-    std::move(callback).Run(std::move(validated_result.error()));
-    return;
+    return std::move(validated_result.error());
   }
 
   gfx::PointF click_point = validated_result.value();
@@ -77,9 +76,8 @@
     }
   }
 
-  mojom::ActionResultPtr result = CreateAndDispatchClick(
-      button, click_count, click_point, frame_->GetWebFrame()->FrameWidget());
-  std::move(callback).Run(std::move(result));
+  return CreateAndDispatchClick(button, click_count, click_point,
+                                frame_->GetWebFrame()->FrameWidget());
 }
 
 std::string ClickTool::DebugString() const {
diff --git a/chrome/renderer/actor/click_tool.h b/chrome/renderer/actor/click_tool.h
index a098dd93..171b981 100644
--- a/chrome/renderer/actor/click_tool.h
+++ b/chrome/renderer/actor/click_tool.h
@@ -12,10 +12,6 @@
 #include "chrome/common/actor.mojom.h"
 #include "chrome/renderer/actor/tool_base.h"
 
-namespace blink {
-class WebMouseEvent;
-}  // namespace blink
-
 namespace content {
 class RenderFrame;
 }  // namespace content
@@ -36,16 +32,13 @@
   ~ClickTool() override;
 
   // actor::ToolBase
-  void Execute(ToolFinishedCallback callback) override;
+  mojom::ActionResultPtr Execute() override;
   std::string DebugString() const override;
 
  private:
   using ValidatedResult = base::expected<gfx::PointF, mojom::ActionResultPtr>;
   ValidatedResult Validate() const;
 
-  void SendMouseUp(blink::WebMouseEvent mouse_event,
-                   ToolFinishedCallback callback);
-
   mojom::ClickActionPtr action_;
 };
 
diff --git a/chrome/renderer/actor/drag_and_release_tool.cc b/chrome/renderer/actor/drag_and_release_tool.cc
index 1b452f12..8430615 100644
--- a/chrome/renderer/actor/drag_and_release_tool.cc
+++ b/chrome/renderer/actor/drag_and_release_tool.cc
@@ -37,11 +37,10 @@
 
 DragAndReleaseTool::~DragAndReleaseTool() = default;
 
-void DragAndReleaseTool::Execute(ToolFinishedCallback callback) {
+mojom::ActionResultPtr DragAndReleaseTool::Execute() {
   ValidatedResult validated_result = Validate();
   if (!validated_result.has_value()) {
-    std::move(callback).Run(std::move(validated_result.error()));
-    return;
+    return std::move(validated_result.error());
   }
 
   gfx::PointF from_point = validated_result->from;
@@ -52,34 +51,27 @@
   // Move and press down the mouse on the from_point.
   if (!InjectMouseEvent(EventType::kMouseMove, from_point,
                         WebMouseEvent::Button::kNoButton)) {
-    std::move(callback).Run(
-        MakeResult(mojom::ActionResultCode::kDragAndReleaseFromMoveSuppressed));
-    return;
+    return MakeResult(
+        mojom::ActionResultCode::kDragAndReleaseFromMoveSuppressed);
   }
 
   if (!InjectMouseEvent(EventType::kMouseDown, from_point,
                         WebMouseEvent::Button::kLeft)) {
-    std::move(callback).Run(
-        MakeResult(mojom::ActionResultCode::kDragAndReleaseDownSuppressed));
-    return;
+    return MakeResult(mojom::ActionResultCode::kDragAndReleaseDownSuppressed);
   }
 
   // Move and release the mouse on the to_point.
   if (!InjectMouseEvent(EventType::kMouseMove, to_point,
                         WebMouseEvent::Button::kLeft)) {
-    std::move(callback).Run(
-        MakeResult(mojom::ActionResultCode::kDragAndReleaseToMoveSuppressed));
-    return;
+    return MakeResult(mojom::ActionResultCode::kDragAndReleaseToMoveSuppressed);
   }
 
   if (!InjectMouseEvent(EventType::kMouseUp, to_point,
                         WebMouseEvent::Button::kLeft)) {
-    std::move(callback).Run(
-        MakeResult(mojom::ActionResultCode::kDragAndReleaseUpSuppressed));
-    return;
+    return MakeResult(mojom::ActionResultCode::kDragAndReleaseUpSuppressed);
   }
 
-  std::move(callback).Run(MakeOkResult());
+  return MakeOkResult();
 }
 
 std::string DragAndReleaseTool::DebugString() const {
diff --git a/chrome/renderer/actor/drag_and_release_tool.h b/chrome/renderer/actor/drag_and_release_tool.h
index c65c660..353412e2 100644
--- a/chrome/renderer/actor/drag_and_release_tool.h
+++ b/chrome/renderer/actor/drag_and_release_tool.h
@@ -35,7 +35,7 @@
   ~DragAndReleaseTool() override;
 
   // actor::ToolBase
-  void Execute(ToolFinishedCallback callback) override;
+  mojom::ActionResultPtr Execute() override;
   std::string DebugString() const override;
 
  private:
diff --git a/chrome/renderer/actor/mouse_move_tool.cc b/chrome/renderer/actor/mouse_move_tool.cc
index 815b384b..49a50c38 100644
--- a/chrome/renderer/actor/mouse_move_tool.cc
+++ b/chrome/renderer/actor/mouse_move_tool.cc
@@ -46,11 +46,10 @@
 
 MouseMoveTool::~MouseMoveTool() = default;
 
-void MouseMoveTool::Execute(ToolFinishedCallback callback) {
+mojom::ActionResultPtr MouseMoveTool::Execute() {
   ValidatedResult validated_result = Validate();
   if (!validated_result.has_value()) {
-    std::move(callback).Run(std::move(validated_result.error()));
-    return;
+    return std::move(validated_result.error());
   }
 
   gfx::PointF move_point = validated_result.value();
@@ -66,11 +65,10 @@
   // Note: KNotHandled probably shouldn't result in an error.
   if (move_result == blink::WebInputEventResult::kNotHandled ||
       move_result == blink::WebInputEventResult::kHandledSuppressed) {
-    std::move(callback).Run(
-        MakeResult(mojom::ActionResultCode::kMouseMoveEventSuppressed));
-    return;
+    return MakeResult(mojom::ActionResultCode::kMouseMoveEventSuppressed);
   }
-  std::move(callback).Run(MakeOkResult());
+
+  return MakeOkResult();
 }
 
 std::string MouseMoveTool::DebugString() const {
diff --git a/chrome/renderer/actor/mouse_move_tool.h b/chrome/renderer/actor/mouse_move_tool.h
index fa8fe139..8baf11dc 100644
--- a/chrome/renderer/actor/mouse_move_tool.h
+++ b/chrome/renderer/actor/mouse_move_tool.h
@@ -30,7 +30,7 @@
   ~MouseMoveTool() override;
 
   // actor::ToolBase
-  void Execute(ToolFinishedCallback callback) override;
+  mojom::ActionResultPtr Execute() override;
   std::string DebugString() const override;
 
  private:
diff --git a/chrome/renderer/actor/scroll_tool.cc b/chrome/renderer/actor/scroll_tool.cc
index a351bd72..6ee0f4f 100644
--- a/chrome/renderer/actor/scroll_tool.cc
+++ b/chrome/renderer/actor/scroll_tool.cc
@@ -36,11 +36,10 @@
 
 ScrollTool::~ScrollTool() = default;
 
-void ScrollTool::Execute(ToolFinishedCallback callback) {
+mojom::ActionResultPtr ScrollTool::Execute() {
   ValidatedResult validated_result = Validate();
   if (!validated_result.has_value()) {
-    std::move(callback).Run(std::move(validated_result.error()));
-    return;
+    return std::move(validated_result.error());
   }
 
   WebElement scrolling_element = validated_result->scroller;
@@ -54,10 +53,9 @@
   scrolling_element.SetScrollOffset(start_offset_css + offset_css);
 
   bool did_scroll = scrolling_element.GetScrollOffset() != start_offset_css;
-  std::move(callback).Run(
-      did_scroll
-          ? MakeOkResult()
-          : MakeResult(mojom::ActionResultCode::kScrollOffsetDidNotChange));
+  return did_scroll
+             ? MakeOkResult()
+             : MakeResult(mojom::ActionResultCode::kScrollOffsetDidNotChange);
 }
 
 std::string ScrollTool::DebugString() const {
diff --git a/chrome/renderer/actor/scroll_tool.h b/chrome/renderer/actor/scroll_tool.h
index 73dd59d3..3bb671402 100644
--- a/chrome/renderer/actor/scroll_tool.h
+++ b/chrome/renderer/actor/scroll_tool.h
@@ -31,7 +31,7 @@
   ~ScrollTool() override;
 
   // actor::ToolBase
-  void Execute(ToolFinishedCallback callback) override;
+  mojom::ActionResultPtr Execute() override;
   std::string DebugString() const override;
 
  private:
diff --git a/chrome/renderer/actor/select_tool.cc b/chrome/renderer/actor/select_tool.cc
index 09a8bb73..e8733a8 100644
--- a/chrome/renderer/actor/select_tool.cc
+++ b/chrome/renderer/actor/select_tool.cc
@@ -36,11 +36,10 @@
 
 SelectTool::~SelectTool() = default;
 
-void SelectTool::Execute(ToolFinishedCallback callback) {
+mojom::ActionResultPtr SelectTool::Execute() {
   ValidatedResult validated_result = Validate();
   if (!validated_result.has_value()) {
-    std::move(callback).Run(std::move(validated_result.error()));
-    return;
+    return std::move(validated_result.error());
   }
 
   WebSelectElement select = validated_result.value().select;
@@ -49,13 +48,12 @@
 
   // Check if the set value is now the current value in the <select>
   if (select.Value() != value) {
-    std::move(callback).Run(
-        MakeResult(mojom::ActionResultCode::kSelectUnexpectedValue,
-                   absl::StrFormat("ValueAfter [%s]", select.Value().Utf8())));
-    return;
+    return MakeResult(
+        mojom::ActionResultCode::kSelectUnexpectedValue,
+        absl::StrFormat("ValueAfter [%s]", select.Value().Utf8()));
   }
 
-  std::move(callback).Run(MakeOkResult());
+  return MakeOkResult();
 }
 
 std::string SelectTool::DebugString() const {
diff --git a/chrome/renderer/actor/select_tool.h b/chrome/renderer/actor/select_tool.h
index b030ec3a..a3b4073c 100644
--- a/chrome/renderer/actor/select_tool.h
+++ b/chrome/renderer/actor/select_tool.h
@@ -28,7 +28,7 @@
   ~SelectTool() override;
 
   // actor::ToolBase
-  void Execute(ToolFinishedCallback callback) override;
+  mojom::ActionResultPtr Execute() override;
   std::string DebugString() const override;
 
  private:
diff --git a/chrome/renderer/actor/tool_base.h b/chrome/renderer/actor/tool_base.h
index e6dd2ce..f82d185 100644
--- a/chrome/renderer/actor/tool_base.h
+++ b/chrome/renderer/actor/tool_base.h
@@ -21,15 +21,14 @@
 
 class ToolBase {
  public:
-  using ToolFinishedCallback = base::OnceCallback<void(mojom::ActionResultPtr)>;
   ToolBase(content::RenderFrame& frame,
            Journal::TaskId task_id,
            Journal& journal)
       : frame_(frame), task_id_(task_id), journal_(journal) {}
   virtual ~ToolBase() = default;
 
-  // Executes the tool. `callback` is invoked with the tool result.
-  virtual void Execute(ToolFinishedCallback callback) = 0;
+  // Executes the tool and returns the result code.
+  virtual mojom::ActionResultPtr Execute() = 0;
 
   // Returns a human readable string representing this tool and its parameters.
   // Used primarily for logging and debugging.
diff --git a/chrome/renderer/actor/tool_executor.cc b/chrome/renderer/actor/tool_executor.cc
index 71ba047..24bc857 100644
--- a/chrome/renderer/actor/tool_executor.cc
+++ b/chrome/renderer/actor/tool_executor.cc
@@ -10,7 +10,9 @@
 #include "base/functional/callback.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
+#include "base/task/sequenced_task_runner.h"
 #include "chrome/common/actor.mojom.h"
+#include "chrome/common/actor/action_result.h"
 #include "chrome/renderer/actor/click_tool.h"
 #include "chrome/renderer/actor/drag_and_release_tool.h"
 #include "chrome/renderer/actor/journal.h"
@@ -33,47 +35,47 @@
 
 void ToolExecutor::InvokeTool(mojom::ToolInvocationPtr request,
                               ToolExecutorCallback callback) {
-  CHECK(!tool_);
+  std::unique_ptr<ToolBase> tool;
   switch (request->action->which()) {
     case actor::mojom::ToolAction::Tag::kClick: {
       // Check the mojom we received is in good shape.
       CHECK(request->action->get_click());
-      tool_ = std::make_unique<ClickTool>(
+      tool = std::make_unique<ClickTool>(
           frame_.get(), request->task_id, journal_.get(),
           std::move(request->action->get_click()));
       break;
     }
     case actor::mojom::ToolAction::Tag::kMouseMove: {
       CHECK(request->action->get_mouse_move());
-      tool_ = std::make_unique<MouseMoveTool>(
+      tool = std::make_unique<MouseMoveTool>(
           frame_.get(), request->task_id, journal_.get(),
           std::move(request->action->get_mouse_move()));
       break;
     }
     case actor::mojom::ToolAction::Tag::kType: {
       CHECK(request->action->get_type());
-      tool_ = std::make_unique<TypeTool>(
-          frame_.get(), request->task_id, journal_.get(),
-          std::move(request->action->get_type()));
+      tool = std::make_unique<TypeTool>(frame_.get(), request->task_id,
+                                        journal_.get(),
+                                        std::move(request->action->get_type()));
       break;
     }
     case actor::mojom::ToolAction::Tag::kScroll: {
       CHECK(request->action->get_scroll());
-      tool_ = std::make_unique<ScrollTool>(
+      tool = std::make_unique<ScrollTool>(
           frame_.get(), request->task_id, journal_.get(),
           std::move(request->action->get_scroll()));
       break;
     }
     case actor::mojom::ToolAction::Tag::kSelect: {
       CHECK(request->action->get_select());
-      tool_ = std::make_unique<SelectTool>(
+      tool = std::make_unique<SelectTool>(
           frame_.get(), request->task_id, journal_.get(),
           std::move(request->action->get_select()));
       break;
     }
     case actor::mojom::ToolAction::Tag::kDragAndRelease: {
       CHECK(request->action->get_drag_and_release());
-      tool_ = std::make_unique<DragAndReleaseTool>(
+      tool = std::make_unique<DragAndReleaseTool>(
           frame_.get(), request->task_id, journal_.get(),
           std::move(request->action->get_drag_and_release()));
       break;
@@ -82,19 +84,9 @@
       NOTREACHED();
   }
 
-  journal_->Log(request->task_id, "Renderer InvokeTool", tool_->DebugString());
+  journal_->Log(request->task_id, "Renderer InvokeTool", tool->DebugString());
 
-  // It's safe to use base::Unretained as tool_ is owned by this object and
-  // tool_ has its own weak factory to manage the callback.
-  tool_->Execute(base::BindOnce(&ToolExecutor::ToolFinished,
-                                base::Unretained(this), std::move(callback)));
-}
-
-void ToolExecutor::ToolFinished(ToolExecutorCallback callback,
-                                mojom::ActionResultPtr result) {
-  CHECK(tool_);
-  // Release current tool so we can accept a new tool invocation.
-  tool_.reset();
+  mojom::ActionResultPtr result = tool->Execute();
   std::move(callback).Run(std::move(result));
 }
 
diff --git a/chrome/renderer/actor/tool_executor.h b/chrome/renderer/actor/tool_executor.h
index 702ad197..a248db1 100644
--- a/chrome/renderer/actor/tool_executor.h
+++ b/chrome/renderer/actor/tool_executor.h
@@ -7,7 +7,6 @@
 
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ref.h"
-#include "base/memory/stack_allocated.h"
 #include "chrome/common/actor.mojom-forward.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
 #include "chrome/renderer/actor/tool_base.h"
@@ -25,13 +24,7 @@
 //
 // This class is responsible for receiving tool request messages and invoking
 // the requested tool in the renderer.
-//
-// WARNING: This class is stack allocated but is written in a way that implies
-// that tools can be asynchronously executed. In practice the tools are
-// synchronous, and there's a lot of re-entrancy.
 class ToolExecutor {
-  STACK_ALLOCATED();
-
  public:
   using ToolExecutorCallback = base::OnceCallback<void(mojom::ActionResultPtr)>;
   explicit ToolExecutor(content::RenderFrame* frame, Journal& journal);
@@ -44,13 +37,9 @@
                   ToolExecutorCallback callback);
 
  private:
-  void ToolFinished(ToolExecutorCallback callback,
-                    mojom::ActionResultPtr result);
-
-  // Raw ref since the executor is currently only stack allocated by the
-  // render frame so it must be outlived.
+  // Raw ref since the executor is owned by the RenderFrameObserver which has
+  // the same lifetime as RenderFrame.
   base::raw_ref<content::RenderFrame> frame_;
-  std::unique_ptr<ToolBase> tool_;
   base::raw_ref<Journal> journal_;
 };
 
diff --git a/chrome/renderer/actor/type_tool.cc b/chrome/renderer/actor/type_tool.cc
index 7e78b63..83a6eaf3 100644
--- a/chrome/renderer/actor/type_tool.cc
+++ b/chrome/renderer/actor/type_tool.cc
@@ -262,11 +262,10 @@
   return MakeOkResult();
 }
 
-void TypeTool::Execute(ToolFinishedCallback callback) {
+mojom::ActionResultPtr TypeTool::Execute() {
   ValidatedResult validated_result = Validate();
   if (!validated_result.has_value()) {
-    std::move(callback).Run(std::move(validated_result.error()));
-    return;
+    return std::move(validated_result.error());
   }
 
   if (std::holds_alternative<gfx::PointF>(validated_result->target)) {
@@ -278,8 +277,7 @@
 
     // Cancel rest of typing if initial click failed.
     if (!IsOk(*result)) {
-      std::move(callback).Run(std::move(result));
-      return;
+      return result;
     }
   } else {
     WebElement element = std::get<blink::WebElement>(validated_result->target);
@@ -310,12 +308,11 @@
   for (const auto& param : validated_result->key_sequence) {
     mojom::ActionResultPtr result = SimulateKeyPress(param);
     if (!IsOk(*result)) {
-      std::move(callback).Run(std::move(result));
-      return;
+      return result;
     }
   }
 
-  std::move(callback).Run(MakeOkResult());
+  return MakeOkResult();
 }
 
 std::string TypeTool::DebugString() const {
diff --git a/chrome/renderer/actor/type_tool.h b/chrome/renderer/actor/type_tool.h
index 4c98751..930aa601 100644
--- a/chrome/renderer/actor/type_tool.h
+++ b/chrome/renderer/actor/type_tool.h
@@ -35,7 +35,7 @@
   ~TypeTool() override;
 
   // actor::ToolBase
-  void Execute(ToolFinishedCallback callback) override;
+  mojom::ActionResultPtr Execute() override;
   std::string DebugString() const override;
 
  private:
diff --git a/chrome/renderer/benchmarking_extension.cc b/chrome/renderer/benchmarking_extension.cc
index 9b12fbaf7..d28a018e 100644
--- a/chrome/renderer/benchmarking_extension.cc
+++ b/chrome/renderer/benchmarking_extension.cc
@@ -12,7 +12,7 @@
 #include "base/profiler/module_cache.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/renderer/render_thread.h"
 #include "v8-local-handle.h"
diff --git a/chrome/renderer/chrome_render_frame_observer.cc b/chrome/renderer/chrome_render_frame_observer.cc
index b6f3621..a92ad797 100644
--- a/chrome/renderer/chrome_render_frame_observer.cc
+++ b/chrome/renderer/chrome_render_frame_observer.cc
@@ -622,8 +622,12 @@
 void ChromeRenderFrameObserver::InvokeTool(
     actor::mojom::ToolInvocationPtr request,
     InvokeToolCallback callback) {
-  actor::ToolExecutor executor(render_frame(), *actor_journal_);
-  executor.InvokeTool(std::move(request), std::move(callback));
+  if (!tool_executor_) {
+    tool_executor_ =
+        std::make_unique<actor::ToolExecutor>(render_frame(), *actor_journal_);
+  }
+
+  tool_executor_->InvokeTool(std::move(request), std::move(callback));
 }
 
 void ChromeRenderFrameObserver::StartActorJournal(
diff --git a/chrome/renderer/chrome_render_frame_observer.h b/chrome/renderer/chrome_render_frame_observer.h
index d785ab23..feecfd5 100644
--- a/chrome/renderer/chrome_render_frame_observer.h
+++ b/chrome/renderer/chrome_render_frame_observer.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_RENDERER_CHROME_RENDER_FRAME_OBSERVER_H_
 #define CHROME_RENDERER_CHROME_RENDER_FRAME_OBSERVER_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -21,6 +22,7 @@
 
 #if !BUILDFLAG(IS_ANDROID)
 #include "chrome/common/actor.mojom.h"
+#include "chrome/renderer/actor/tool_executor.h"
 #endif
 
 class SkBitmap;
@@ -192,6 +194,10 @@
   std::vector<std::u16string> webui_javascript_;
 #endif
 
+#if !BUILDFLAG(IS_ANDROID)
+  std::unique_ptr<actor::ToolExecutor> tool_executor_;
+#endif
+
   mojo::AssociatedReceiverSet<chrome::mojom::ChromeRenderFrame> receivers_;
 
   service_manager::BinderRegistry registry_;
diff --git a/chrome/services/sharing/nearby/platform/wifi_direct_medium_unittest.cc b/chrome/services/sharing/nearby/platform/wifi_direct_medium_unittest.cc
index 91994c0..6c3c24e67 100644
--- a/chrome/services/sharing/nearby/platform/wifi_direct_medium_unittest.cc
+++ b/chrome/services/sharing/nearby/platform/wifi_direct_medium_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/task/thread_pool.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
+#include "base/threading/thread_restrictions.h"
 #include "chrome/services/sharing/nearby/platform/wifi_direct_server_socket.h"
 #include "chromeos/ash/services/nearby/public/cpp/fake_firewall_hole_factory.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
diff --git a/chrome/services/sharing/nearby/platform/wifi_direct_server_socket_unittest.cc b/chrome/services/sharing/nearby/platform/wifi_direct_server_socket_unittest.cc
index b19992c..8a573b5 100644
--- a/chrome/services/sharing/nearby/platform/wifi_direct_server_socket_unittest.cc
+++ b/chrome/services/sharing/nearby/platform/wifi_direct_server_socket_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/task/thread_pool.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
+#include "base/threading/thread_restrictions.h"
 #include "chromeos/ash/services/nearby/public/cpp/fake_firewall_hole.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/base/net_errors.h"
diff --git a/chrome/services/sharing/nearby/platform/wifi_direct_socket_unittest.cc b/chrome/services/sharing/nearby/platform/wifi_direct_socket_unittest.cc
index 2346a2d..2e44bd0 100644
--- a/chrome/services/sharing/nearby/platform/wifi_direct_socket_unittest.cc
+++ b/chrome/services/sharing/nearby/platform/wifi_direct_socket_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/task/thread_pool.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
+#include "base/threading/thread_restrictions.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "net/socket/stream_socket.h"
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 578fb52..08ddc94 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -182,12 +182,6 @@
     # Unable to run chrome-headless-shell with logging enabled on Mac. See
     # crbug.com/1011000.
     'ChromeLogPathCapabilityTest.testChromeLogPath',
-    # https://crbug.com/chromedriver/4631
-    # chrome-headless-shell does not set the window rect as requested.
-    'ChromeDriverTest.testWindowMinimize',
-    'ChromeDriverTest.testWindowPosition',
-    'ChromeDriverTest.testWindowRect',
-    'ChromeDriverTest.testWindowSize',
     # https://crbug.com/chromedriver/4632
     # chrome-headless-shell ignores the selected range while inserting the text
     'ChromeDriverW3cTest.testSendKeysToElementDoesNotAppend',
diff --git a/chrome/test/data/actor/cross_document_nav.html b/chrome/test/data/actor/cross_document_nav.html
new file mode 100644
index 0000000..50f58d7
--- /dev/null
+++ b/chrome/test/data/actor/cross_document_nav.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width, minimum-scale=1.0">
+    <title>Cross Document Navigation</title>
+  </head>
+  <body>
+    <a id="link" href="simple_iframe.html">Go To Next Page</a>
+  </body>
+</html>
diff --git a/chrome/test/data/webui/privacy_sandbox/base_dialog_browsertest.cc b/chrome/test/data/webui/privacy_sandbox/base_dialog_browsertest.cc
index 92cfa6b..c5ff182 100644
--- a/chrome/test/data/webui/privacy_sandbox/base_dialog_browsertest.cc
+++ b/chrome/test/data/webui/privacy_sandbox/base_dialog_browsertest.cc
@@ -67,4 +67,8 @@
 IN_PROC_BROWSER_TEST_P(PrivacySandboxBaseDialogMochaTest, EEAConsentAndNotice) {
   RunTestSuite("EEAConsentAndNotice");
 }
+
+IN_PROC_BROWSER_TEST_P(PrivacySandboxBaseDialogMochaTest, BaseDialogLearnMore) {
+  RunTestSuite("BaseDialogLearnMore");
+}
 }  // namespace
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 067eb10..7336c84c 100644
--- a/chrome/test/data/webui/privacy_sandbox/base_dialog_test.ts
+++ b/chrome/test/data/webui/privacy_sandbox/base_dialog_test.ts
@@ -6,13 +6,16 @@
 import 'chrome://privacy-sandbox-base-dialog/topics_consent_notice.js';
 import 'chrome://privacy-sandbox-base-dialog/protected_audience_measurement_notice.js';
 import 'chrome://privacy-sandbox-base-dialog/three_ads_apis_notice.js';
+import 'chrome://privacy-sandbox-base-dialog/base_dialog_learn_more.js';
 
 import type {BaseDialogPageRemote} from 'chrome://privacy-sandbox-base-dialog/base_dialog.mojom-webui.js';
 import type {BaseDialogApp} from 'chrome://privacy-sandbox-base-dialog/base_dialog_app.js';
 import {BaseDialogBrowserProxy} from 'chrome://privacy-sandbox-base-dialog/base_dialog_browser_proxy.js';
+import type {BaseDialogLearnMore} from 'chrome://privacy-sandbox-base-dialog/base_dialog_learn_more.js';
 import {PrivacySandboxNotice, PrivacySandboxNoticeEvent} from 'chrome://privacy-sandbox-base-dialog/notice.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import type {TestBaseDialogPageHandler} from './test_base_dialog_browser_proxy.js';
 import {TestBaseDialogBrowserProxy} from './test_base_dialog_browser_proxy.js';
@@ -257,3 +260,31 @@
         PrivacySandboxNoticeEvent.kAck, testHandler);
   });
 });
+
+suite('BaseDialogLearnMore', function() {
+  let learnMoreElement: BaseDialogLearnMore;
+
+  setup(function() {
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    learnMoreElement = document.createElement('base-dialog-learn-more');
+    document.body.appendChild(learnMoreElement);
+  });
+
+  test('ExpandAndCollapse', async function() {
+    const expandButton =
+        learnMoreElement.shadowRoot.querySelector('cr-expand-button');
+    assertTrue(!!expandButton);
+    // Ensure it's initially collapsed
+    const collapse = learnMoreElement.shadowRoot.querySelector('cr-collapse');
+    assertTrue(!!collapse);
+    assertFalse(collapse.opened);
+    // Expand and ensure it's open
+    expandButton.click();
+    await microtasksFinished();
+    assertTrue(collapse.opened);
+    // // Collapse and ensure it's closed
+    expandButton.click();
+    await microtasksFinished();
+    assertFalse(collapse.opened);
+  });
+});
diff --git a/chrome/updater/app/server/win/com_classes_legacy_unittest.cc b/chrome/updater/app/server/win/com_classes_legacy_unittest.cc
index 480fd37..dd4ee73 100644
--- a/chrome/updater/app/server/win/com_classes_legacy_unittest.cc
+++ b/chrome/updater/app/server/win/com_classes_legacy_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
 #include "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index bd7c647..fb9b5d7c 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -237,6 +237,7 @@
     "//chromecast/media/cdm:cdm_origin_provider",
     "//chromecast/media/common:media_pipeline_backend_manager",
     "//chromecast/media/service",
+    "//chromecast/media/service:video_geometry_setter_service",
     "//chromecast/media/service/mojom",
     "//chromecast/net",
     "//chromecast/net:connectivity_checker",
diff --git a/chromecast/media/service/BUILD.gn b/chromecast/media/service/BUILD.gn
index 6b5fd743..022afb67 100644
--- a/chromecast/media/service/BUILD.gn
+++ b/chromecast/media/service/BUILD.gn
@@ -10,8 +10,6 @@
     "cast_mojo_media_client.h",
     "cast_renderer.cc",
     "cast_renderer.h",
-    "video_geometry_setter_service.cc",
-    "video_geometry_setter_service.h",
   ]
 
   public_deps = [
@@ -20,6 +18,7 @@
   ]
 
   deps = [
+    ":video_geometry_setter_service",
     "//base",
     "//chromecast/base",
     "//chromecast/common/mojom",
@@ -30,3 +29,15 @@
     "//ui/gfx/geometry",
   ]
 }
+
+cast_source_set("video_geometry_setter_service") {
+  sources = [
+    "video_geometry_setter_service.cc",
+    "video_geometry_setter_service.h",
+  ]
+  deps = [
+    "//base",
+    "//chromecast/media/service/mojom",
+    "//mojo/public/cpp/bindings",
+  ]
+}
diff --git a/chromecast/starboard/DEPS b/chromecast/starboard/DEPS
index 51e3da6..18d9b76 100644
--- a/chromecast/starboard/DEPS
+++ b/chromecast/starboard/DEPS
@@ -2,7 +2,11 @@
   "+chromecast/media",
   "+google_apis",
   "+media/base",
+  "+mojo/core/embedder",
+  "+mojo/public/cpp/bindings",
   "+ui/base",
+  "+ui/display",
   "+ui/events",
+  "+ui/gfx",
   "+ui/platform_window",
 ]
diff --git a/chromecast/starboard/media/BUILD.gn b/chromecast/starboard/media/BUILD.gn
index 08250a3..abe5bc3 100644
--- a/chromecast/starboard/media/BUILD.gn
+++ b/chromecast/starboard/media/BUILD.gn
@@ -15,5 +15,8 @@
     "//chromecast/starboard/media/media:starboard_resampler_test",
     "//chromecast/starboard/media/media:starboard_video_decoder_test",
     "//chromecast/starboard/media/media:starboard_video_plane_test",
+    "//chromecast/starboard/media/renderer:chromium_starboard_conversions_test",
+    "//chromecast/starboard/media/renderer:client_stats_tracker_test",
+    "//chromecast/starboard/media/renderer:demuxer_stream_reader_test",
   ]
 }
diff --git a/chromecast/starboard/media/media/BUILD.gn b/chromecast/starboard/media/media/BUILD.gn
index 1f9fab3..1d8944b 100644
--- a/chromecast/starboard/media/media/BUILD.gn
+++ b/chromecast/starboard/media/media/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//chromecast/build/tests/cast_test.gni")
 import("//chromecast/chromecast.gni")
 import("//chromecast/starboard/starboard.gni")
 import("//testing/test.gni")
@@ -11,19 +12,23 @@
       "//chromecast/starboard/third_party/starboard/sabi/base_configuration.gni")
 }
 
+# This is necessary until code under //chromecast/starboard/media/renderer is
+# hooked up to production code. This way, gn can find those test targets.
+cast_test_group("starboard_renderer_tests") {
+  tests = [
+    "//chromecast/starboard/media/renderer:chromium_starboard_conversions_test",
+  ]
+}
+
 source_set("starboard") {
   sources = [
     "cast_media_starboard.cc",
-    "drm_util.cc",
-    "drm_util.h",
     "media_pipeline_backend_starboard.cc",
     "media_pipeline_backend_starboard.h",
     "starboard_audio_decoder.cc",
     "starboard_audio_decoder.h",
     "starboard_decoder.cc",
     "starboard_decoder.h",
-    "starboard_resampler.cc",
-    "starboard_resampler.h",
     "starboard_video_decoder.cc",
     "starboard_video_decoder.h",
     "starboard_video_plane.cc",
@@ -31,8 +36,10 @@
     "starboard_volume_control.cc",
   ]
   deps = [
+    ":drm_util",
     ":mime_utils",
     ":starboard_api_wrapper",
+    ":starboard_resampler",
     "//base",
     "//chromecast/base",
     "//chromecast/public",
@@ -46,6 +53,31 @@
   ]
 }
 
+source_set("starboard_resampler") {
+  sources = [
+    "starboard_resampler.cc",
+    "starboard_resampler.h",
+  ]
+  deps = [
+    "//base",
+    "//chromecast/public/media",
+    "//chromecast/starboard/chromecast/starboard_cast_api:cast_starboard_api_types",
+    "//media",
+  ]
+}
+
+source_set("drm_util") {
+  sources = [
+    "drm_util.cc",
+    "drm_util.h",
+  ]
+  deps = [
+    ":starboard_api_wrapper",
+    "//chromecast/public/media",
+    "//media",
+  ]
+}
+
 source_set("mock_starboard_api_wrapper") {
   testonly = true
   sources = [
@@ -96,9 +128,23 @@
     "mime_utils.cc",
     "mime_utils.h",
   ]
+  public_deps = [
+    "//chromecast/public/media",
+    "//media",
+  ]
   deps = [
     "//base",
-    "//chromecast/public/media",
+    "//chromecast/media/base:media_codec_support",
+  ]
+}
+
+source_set("test_matchers") {
+  testonly = true
+  public = [ "test_matchers.h" ]
+  sources = [ "test_matchers.cc" ]
+  public_deps = [
+    ":starboard_api_wrapper",
+    "//testing/gmock",
   ]
 }
 
@@ -160,6 +206,7 @@
     ":mock_starboard_api_wrapper",
     ":starboard",
     ":starboard_api_wrapper",
+    ":starboard_resampler",
     "//base/test:run_all_unittests",
     "//base/test:test_support",
     "//chromecast/media/base",
@@ -188,8 +235,23 @@
   deps = [
     ":mime_utils",
     "//base/test:run_all_unittests",
+    "//chromecast/media/base:media_codec_support",
     "//chromecast/public/media",
     "//testing/gmock",
     "//testing/gtest",
   ]
 }
+
+test("drm_util_test") {
+  sources = [ "drm_util_test.cc" ]
+  deps = [
+    ":drm_util",
+    ":test_matchers",
+    "//base",
+    "//base/test:run_all_unittests",
+    "//chromecast/media/cma/base",
+    "//media",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chromecast/starboard/media/media/drm_util.cc b/chromecast/starboard/media/media/drm_util.cc
index 532b435..549fb42 100644
--- a/chromecast/starboard/media/media/drm_util.cc
+++ b/chromecast/starboard/media/media/drm_util.cc
@@ -15,9 +15,9 @@
 
 // Rather than hard-coding values here, we simply read the length of the
 // relevant arrays in StarboardDrmSampleInfo.
-constexpr int kMaxIvLength =
+constexpr size_t kMaxIvLength =
     std::size(StarboardDrmSampleInfo{}.initialization_vector);
-constexpr int kMaxIdLength = std::size(StarboardDrmSampleInfo{}.identifier);
+constexpr size_t kMaxIdLength = std::size(StarboardDrmSampleInfo{}.identifier);
 
 DrmInfoWrapper::DrmInfoWrapper() = default;
 
@@ -114,5 +114,95 @@
   return DrmInfoWrapper(std::move(drm_info), std::move(subsample_mappings));
 }
 
+DrmInfoWrapper DrmInfoWrapper::Create(const ::media::DecoderBuffer& buffer) {
+  if (buffer.decrypt_config() == nullptr) {
+    return DrmInfoWrapper();
+  }
+
+  // Populate drm_sample_info.
+  auto drm_info = std::make_unique<StarboardDrmSampleInfo>();
+
+  const ::media::DecryptConfig& decrypt_config = *buffer.decrypt_config();
+  switch (decrypt_config.encryption_scheme()) {
+    case ::media::EncryptionScheme::kUnencrypted:
+      return DrmInfoWrapper();
+    case ::media::EncryptionScheme::kCenc:
+      drm_info->encryption_scheme =
+          StarboardDrmEncryptionScheme::kStarboardDrmEncryptionSchemeAesCtr;
+      break;
+    case ::media::EncryptionScheme::kCbcs:
+      drm_info->encryption_scheme =
+          StarboardDrmEncryptionScheme::kStarboardDrmEncryptionSchemeAesCbc;
+      break;
+    default:
+      LOG(ERROR) << "Unsupported DRM encryption scheme: "
+                 << decrypt_config.encryption_scheme();
+      return DrmInfoWrapper();
+  }
+
+  // Populate drm_sample_info.
+  if (decrypt_config.HasPattern()) {
+    drm_info->encryption_pattern.crypt_byte_block =
+        decrypt_config.encryption_pattern()->crypt_byte_block();
+    drm_info->encryption_pattern.skip_byte_block =
+        decrypt_config.encryption_pattern()->skip_byte_block();
+  }
+
+  size_t iv_size = decrypt_config.iv().size();
+  if (iv_size > kMaxIvLength) {
+    LOG(ERROR)
+        << "Encrypted buffer contained too many initialization vector values "
+           "(max supported by Starboard is "
+        << kMaxIvLength << "): " << iv_size;
+    iv_size = kMaxIvLength;
+  }
+
+  // Populate drm_info->initialization_vector.
+  base::span<uint8_t>(drm_info->initialization_vector)
+      .first(iv_size)
+      .copy_from_nonoverlapping(
+          base::as_byte_span(decrypt_config.iv()).first(iv_size));
+  drm_info->initialization_vector_size = iv_size;
+
+  size_t id_size = decrypt_config.key_id().size();
+  if (id_size > kMaxIdLength) {
+    LOG(ERROR) << "Encrypted buffer contained too many key ID vector values "
+                  "(max supported by Starboard is "
+               << kMaxIdLength << "): " << id_size;
+    id_size = kMaxIdLength;
+  }
+
+  // Populate drm_info->identifier.
+  base::span<uint8_t>(drm_info->identifier)
+      .first(id_size)
+      .copy_from_nonoverlapping(
+          base::as_byte_span(decrypt_config.key_id()).first(id_size));
+  drm_info->identifier_size = id_size;
+
+  // Populate subsample_mappings.
+  auto subsample_mappings =
+      std::make_unique<std::vector<StarboardDrmSubSampleMapping>>();
+  subsample_mappings->reserve(decrypt_config.subsamples().size());
+  for (const ::media::SubsampleEntry& subsample : decrypt_config.subsamples()) {
+    StarboardDrmSubSampleMapping mapping;
+    mapping.clear_byte_count = subsample.clear_bytes;
+    mapping.encrypted_byte_count = subsample.cypher_bytes;
+    subsample_mappings->push_back(std::move(mapping));
+  }
+
+  if (subsample_mappings->empty()) {
+    // DecryptConfig may contain 0 subsamples if all content is encrypted. Map
+    // this case to a single fully-encrypted "subsample", since Starboard
+    // requires at least one subsample.
+    subsample_mappings->push_back(
+        {.clear_byte_count = 0,
+         .encrypted_byte_count = static_cast<int32_t>(buffer.size())});
+  }
+  drm_info->subsample_mapping =
+      base::span<const StarboardDrmSubSampleMapping>(*subsample_mappings);
+
+  return DrmInfoWrapper(std::move(drm_info), std::move(subsample_mappings));
+}
+
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/starboard/media/media/drm_util.h b/chromecast/starboard/media/media/drm_util.h
index 3cc55c020..8bdf486 100644
--- a/chromecast/starboard/media/media/drm_util.h
+++ b/chromecast/starboard/media/media/drm_util.h
@@ -11,6 +11,7 @@
 #include "chromecast/public/media/cast_decoder_buffer.h"
 #include "chromecast/public/media/cast_decrypt_config.h"
 #include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "media/base/decoder_buffer.h"
 
 namespace chromecast {
 namespace media {
@@ -26,11 +27,16 @@
 // Instances of this class should be created via DrmInfoWrapper::Create().
 class DrmInfoWrapper {
  public:
+  // TODO(antoniori): deprecate this version after moving to StarboardRenderer.
+  //
   // Extracts and returns DRM info from `buffer`. If the buffer is not
-  // encrypted, the StarboardDrmSampleInfo returned by GetDrmSampleInfo will be
-  // null.
+  // encrypted, GetDrmSampleInfo() will return null.
   static DrmInfoWrapper Create(const CastDecoderBuffer& buffer);
 
+  // Extracts and returns DRM info from `buffer`. If the buffer is not
+  // encrypted, GetDrmSampleInfo() will return null.
+  static DrmInfoWrapper Create(const ::media::DecoderBuffer& buffer);
+
   // DrmInfoWrapper is movable but not copyable.
   DrmInfoWrapper(DrmInfoWrapper&& other);
   DrmInfoWrapper& operator=(DrmInfoWrapper&& other);
diff --git a/chromecast/starboard/media/media/drm_util_test.cc b/chromecast/starboard/media/media/drm_util_test.cc
new file mode 100644
index 0000000..2c9747b02
--- /dev/null
+++ b/chromecast/starboard/media/media/drm_util_test.cc
@@ -0,0 +1,211 @@
+// 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 "chromecast/starboard/media/media/drm_util.h"
+
+#include <array>
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+
+#include "base/containers/span.h"
+#include "base/memory/scoped_refptr.h"
+#include "chromecast/media/cma/base/decoder_buffer_adapter.h"
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "chromecast/starboard/media/media/test_matchers.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/decrypt_config.h"
+#include "media/base/encryption_pattern.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+namespace {
+
+using ::testing::IsNull;
+using ::testing::Pointee;
+
+constexpr auto kDefaultBufferData =
+    std::to_array<uint8_t>({1, 2, 3, 4, 5, 6, 7});
+
+// Creates a chromium buffer from the given decrypt config and data.
+scoped_refptr<::media::DecoderBuffer> CreateChromiumBuffer(
+    std::unique_ptr<::media::DecryptConfig> decrypt_config,
+    base::span<const uint8_t> data = kDefaultBufferData) {
+  scoped_refptr<::media::DecoderBuffer> buffer =
+      ::media::DecoderBuffer::CopyFrom(data);
+  CHECK(buffer);
+  buffer->set_decrypt_config(std::move(decrypt_config));
+  return buffer;
+}
+
+TEST(DrmUtilTest, UnencryptedBufferHasNullDrmSampleInfo) {
+  scoped_refptr<::media::DecoderBuffer> chromium_buffer =
+      CreateChromiumBuffer(/*decrypt_config=*/nullptr);
+  CHECK(chromium_buffer);
+
+  EXPECT_THAT(DrmInfoWrapper::Create(*chromium_buffer).GetDrmSampleInfo(),
+              IsNull());
+
+  // Check the version that uses CastDecoderBuffer.
+  auto cast_buffer =
+      base::MakeRefCounted<DecoderBufferAdapter>(chromium_buffer);
+  CHECK(cast_buffer);
+  EXPECT_THAT(DrmInfoWrapper::Create(*cast_buffer).GetDrmSampleInfo(),
+              IsNull());
+}
+
+TEST(DrmUtilTest, CreatesCencDrmInfo) {
+  constexpr std::string_view kId = "drm_id";
+  constexpr std::string_view kIv = "0123456789abcdef";
+  CHECK_EQ(kIv.size(),
+           static_cast<size_t>(::media::DecryptConfig::kDecryptionKeySize));
+  const ::media::SubsampleEntry subsample(2, 5);
+  StarboardDrmSubSampleMapping sb_subsample;
+  sb_subsample.clear_byte_count = subsample.clear_bytes;
+  sb_subsample.encrypted_byte_count = subsample.cypher_bytes;
+
+  std::unique_ptr<::media::DecryptConfig> decrypt_config =
+      ::media::DecryptConfig::CreateCencConfig(std::string(kId),
+                                               std::string(kIv), {subsample});
+  CHECK(decrypt_config);
+
+  scoped_refptr<::media::DecoderBuffer> chromium_buffer =
+      CreateChromiumBuffer(std::move(decrypt_config));
+  CHECK(chromium_buffer);
+
+  DrmInfoWrapper wrapper = DrmInfoWrapper::Create(*chromium_buffer);
+
+  StarboardDrmSampleInfo expected_drm_info;
+  expected_drm_info.encryption_scheme =
+      StarboardDrmEncryptionScheme::kStarboardDrmEncryptionSchemeAesCtr;
+  expected_drm_info.encryption_pattern.crypt_byte_block = 0;
+  expected_drm_info.encryption_pattern.skip_byte_block = 0;
+  base::span<uint8_t>(expected_drm_info.initialization_vector)
+      .copy_from_nonoverlapping(base::as_byte_span(kIv));
+  expected_drm_info.initialization_vector_size = kIv.size();
+  base::span<uint8_t>(expected_drm_info.identifier)
+      .first<kId.size()>()
+      .copy_from_nonoverlapping(base::as_byte_span(kId));
+  expected_drm_info.identifier_size = kId.size();
+  expected_drm_info.subsample_mapping = base::span_from_ref(sb_subsample);
+
+  EXPECT_THAT(DrmInfoWrapper::Create(*chromium_buffer).GetDrmSampleInfo(),
+              Pointee(MatchesDrmInfo(expected_drm_info)));
+
+  // Check the version that uses CastDecoderBuffer.
+  auto cast_buffer =
+      base::MakeRefCounted<DecoderBufferAdapter>(chromium_buffer);
+  CHECK(cast_buffer);
+  EXPECT_THAT(DrmInfoWrapper::Create(*cast_buffer).GetDrmSampleInfo(),
+              Pointee(MatchesDrmInfo(expected_drm_info)));
+}
+
+TEST(DrmUtilTest, CreatesCbcsDrmInfo) {
+  constexpr std::string_view kId = "drm_id_2";
+  constexpr std::string_view kIv = "abcdefghijklmnop";
+  CHECK_EQ(kIv.size(),
+           static_cast<size_t>(::media::DecryptConfig::kDecryptionKeySize));
+  const ::media::EncryptionPattern encryption_pattern(10, 20);
+  const ::media::SubsampleEntry subsample(1, 6);
+  StarboardDrmSubSampleMapping sb_subsample;
+  sb_subsample.clear_byte_count = subsample.clear_bytes;
+  sb_subsample.encrypted_byte_count = subsample.cypher_bytes;
+
+  std::unique_ptr<::media::DecryptConfig> decrypt_config =
+      ::media::DecryptConfig::CreateCbcsConfig(
+          std::string(kId), std::string(kIv), {subsample}, encryption_pattern);
+  CHECK(decrypt_config);
+
+  scoped_refptr<::media::DecoderBuffer> chromium_buffer =
+      CreateChromiumBuffer(std::move(decrypt_config));
+  CHECK(chromium_buffer);
+
+  DrmInfoWrapper wrapper = DrmInfoWrapper::Create(*chromium_buffer);
+
+  StarboardDrmSampleInfo expected_drm_info;
+  expected_drm_info.encryption_scheme =
+      StarboardDrmEncryptionScheme::kStarboardDrmEncryptionSchemeAesCbc;
+  expected_drm_info.encryption_pattern.crypt_byte_block =
+      encryption_pattern.crypt_byte_block();
+  expected_drm_info.encryption_pattern.skip_byte_block =
+      encryption_pattern.skip_byte_block();
+  base::span<uint8_t>(expected_drm_info.initialization_vector)
+      .copy_from_nonoverlapping(base::as_byte_span(kIv));
+  expected_drm_info.initialization_vector_size = kIv.size();
+  base::span<uint8_t>(expected_drm_info.identifier)
+      .first<kId.size()>()
+      .copy_from_nonoverlapping(base::as_byte_span(kId));
+  expected_drm_info.identifier_size = kId.size();
+  expected_drm_info.subsample_mapping = base::span_from_ref(sb_subsample);
+
+  EXPECT_THAT(DrmInfoWrapper::Create(*chromium_buffer).GetDrmSampleInfo(),
+              Pointee(MatchesDrmInfo(expected_drm_info)));
+
+  // Check the version that uses CastDecoderBuffer.
+  auto cast_buffer =
+      base::MakeRefCounted<DecoderBufferAdapter>(chromium_buffer);
+  CHECK(cast_buffer);
+  EXPECT_THAT(DrmInfoWrapper::Create(*cast_buffer).GetDrmSampleInfo(),
+              Pointee(MatchesDrmInfo(expected_drm_info)));
+}
+
+TEST(DrmUtilTest, HandlesEmptySubsampleMappings) {
+  // Chromium buffers might not specify a subsample mapping. We should assume
+  // that the entire buffer is encrypted, in that case.
+  constexpr auto kBufferData = std::to_array<uint8_t>({7, 8, 9});
+  constexpr std::string_view kId = "drm_id";
+  constexpr std::string_view kIv = "0123456789abcdef";
+  CHECK_EQ(kIv.size(),
+           static_cast<size_t>(::media::DecryptConfig::kDecryptionKeySize));
+
+  std::unique_ptr<::media::DecryptConfig> decrypt_config =
+      ::media::DecryptConfig::CreateCencConfig(
+          std::string(kId), std::string(kIv), /*subsamples=*/{});
+  CHECK(decrypt_config);
+
+  scoped_refptr<::media::DecoderBuffer> chromium_buffer =
+      CreateChromiumBuffer(std::move(decrypt_config), /*data=*/kBufferData);
+  CHECK(chromium_buffer);
+
+  DrmInfoWrapper wrapper = DrmInfoWrapper::Create(*chromium_buffer);
+
+  StarboardDrmSampleInfo expected_drm_info;
+  expected_drm_info.encryption_scheme =
+      StarboardDrmEncryptionScheme::kStarboardDrmEncryptionSchemeAesCtr;
+  expected_drm_info.encryption_pattern.crypt_byte_block = 0;
+  expected_drm_info.encryption_pattern.skip_byte_block = 0;
+  base::span<uint8_t>(expected_drm_info.initialization_vector)
+      .copy_from_nonoverlapping(base::as_byte_span(kIv));
+  expected_drm_info.initialization_vector_size = kIv.size();
+  base::span<uint8_t>(expected_drm_info.identifier)
+      .first<kId.size()>()
+      .copy_from_nonoverlapping(base::as_byte_span(kId));
+  expected_drm_info.identifier_size = kId.size();
+
+  // There should be a single subsample mapping specifying that the entire
+  // buffer is encrypted.
+  StarboardDrmSubSampleMapping sb_subsample;
+  sb_subsample.clear_byte_count = 0;
+  sb_subsample.encrypted_byte_count = kBufferData.size();
+  expected_drm_info.subsample_mapping = base::span_from_ref(sb_subsample);
+
+  EXPECT_THAT(DrmInfoWrapper::Create(*chromium_buffer).GetDrmSampleInfo(),
+              Pointee(MatchesDrmInfo(expected_drm_info)));
+
+  // Check the version that uses CastDecoderBuffer. The cast code that converts
+  // from chromium structs -> cast structs should have performed the same logic
+  // of creating a single subsample mapping.
+  auto cast_buffer =
+      base::MakeRefCounted<DecoderBufferAdapter>(chromium_buffer);
+  CHECK(cast_buffer);
+  EXPECT_THAT(DrmInfoWrapper::Create(*cast_buffer).GetDrmSampleInfo(),
+              Pointee(MatchesDrmInfo(expected_drm_info)));
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/media/mime_utils.cc b/chromecast/starboard/media/media/mime_utils.cc
index f134982..b20afe78 100644
--- a/chromecast/starboard/media/media/mime_utils.cc
+++ b/chromecast/starboard/media/media/mime_utils.cc
@@ -4,12 +4,14 @@
 
 #include "chromecast/starboard/media/media/mime_utils.h"
 
+#include <limits>
 #include <string>
 #include <string_view>
 
 #include "base/containers/fixed_flat_map.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
+#include "chromecast/media/base/media_codec_support.h"
 
 namespace chromecast {
 namespace media {
@@ -190,6 +192,18 @@
   }
 }
 
+std::string GetMimeType(::media::VideoCodec codec,
+                        ::media::VideoCodecProfile profile,
+                        uint32_t level) {
+  // Ensure that `level` can be converted to int32_t safely.
+  if (int64_t{level} > int64_t{std::numeric_limits<int32_t>::max()}) {
+    LOG(ERROR) << "Invalid codec level: " << level;
+    return "";
+  }
+  return GetMimeType(ToCastVideoCodec(codec, profile),
+                     ToCastVideoProfile(profile), static_cast<int32_t>(level));
+}
+
 std::string GetMimeType(AudioCodec codec) {
   if (auto it = kAudioCodecToMime.find(codec); it != kAudioCodecToMime.end()) {
     return std::string(it->second);
@@ -197,5 +211,9 @@
   return "";
 }
 
+std::string GetMimeType(::media::AudioCodec codec) {
+  return GetMimeType(ToCastAudioCodec(codec));
+}
+
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/starboard/media/media/mime_utils.h b/chromecast/starboard/media/media/mime_utils.h
index f60baec..fe0e36fc 100644
--- a/chromecast/starboard/media/media/mime_utils.h
+++ b/chromecast/starboard/media/media/mime_utils.h
@@ -9,6 +9,8 @@
 #include <string>
 
 #include "chromecast/public/media/decoder_config.h"
+#include "media/base/audio_codecs.h"
+#include "media/base/video_codecs.h"
 
 namespace chromecast {
 namespace media {
@@ -21,6 +23,11 @@
 // If a MIME type cannot be determined, an empty string is returned.
 std::string GetMimeType(VideoCodec codec, VideoProfile profile, int32_t level);
 
+// Same as above, but uses chromium enums.
+std::string GetMimeType(::media::VideoCodec codec,
+                        ::media::VideoCodecProfile profile,
+                        uint32_t level);
+
 // Returns the MIME string for the given audio codec. Container is guessed in a
 // way that should be compatible with Starboard's checks (e.g. for opus we guess
 // webm). Ideally Starboard should not care about the container, since they do
@@ -29,6 +36,9 @@
 // If a MIME type cannot be determined, an empty string is returned.
 std::string GetMimeType(AudioCodec codec);
 
+// Same as above, but uses the chromium version of the codec enum.
+std::string GetMimeType(::media::AudioCodec codec);
+
 }  // namespace media
 }  // namespace chromecast
 
diff --git a/chromecast/starboard/media/media/mime_utils_test.cc b/chromecast/starboard/media/media/mime_utils_test.cc
index 2b46ee7..40877e2 100644
--- a/chromecast/starboard/media/media/mime_utils_test.cc
+++ b/chromecast/starboard/media/media/mime_utils_test.cc
@@ -7,7 +7,9 @@
 #include <string>
 #include <vector>
 
+#include "chromecast/media/base/media_codec_support.h"
 #include "chromecast/public/media/decoder_config.h"
+#include "media/base/video_codecs.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -19,13 +21,13 @@
 
 // Holds video codec info and the corresponding MIME type.
 struct VideoCodecInfoAndMime {
-  CodecProfileLevel codec_profile_level;
+  ::media::CodecProfileLevel codec_profile_level;
   std::string mime;
 };
 
 // Holds audio codec info and the corresponding MIME type.
 struct AudioCodecInfoAndMime {
-  AudioCodec codec;
+  ::media::AudioCodec codec;
   std::string mime;
 };
 
@@ -36,154 +38,212 @@
   std::vector<VideoCodecInfoAndMime> out;
 
   // h.264 baseline profile.
-  out.push_back({.codec_profile_level = {.codec = kCodecH264,
-                                         .profile = kH264Baseline,
-                                         .level = 30},
-                 .mime = R"-(video/mp4; codecs="avc1.42E01E")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecH264,
-                                         .profile = kH264Baseline,
-                                         .level = 31},
-                 .mime = R"-(video/mp4; codecs="avc1.42E01F")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kH264,
+            .profile = ::media::VideoCodecProfile::H264PROFILE_BASELINE,
+            .level = 30},
+       .mime = R"-(video/mp4; codecs="avc1.42E01E")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kH264,
+            .profile = ::media::VideoCodecProfile::H264PROFILE_BASELINE,
+            .level = 31},
+       .mime = R"-(video/mp4; codecs="avc1.42E01F")-"});
 
   // h.264 main profile.
-  out.push_back({.codec_profile_level = {.codec = kCodecH264,
-                                         .profile = kH264Main,
-                                         .level = 31},
-                 .mime = R"-(video/mp4; codecs="avc1.4D401F")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecH264,
-                                         .profile = kH264Main,
-                                         .level = 40},
-                 .mime = R"-(video/mp4; codecs="avc1.4D4028")-"});
+  out.push_back(
+      {.codec_profile_level = {.codec = ::media::VideoCodec::kH264,
+                               .profile =
+                                   ::media::VideoCodecProfile::H264PROFILE_MAIN,
+                               .level = 31},
+       .mime = R"-(video/mp4; codecs="avc1.4D401F")-"});
+  out.push_back(
+      {.codec_profile_level = {.codec = ::media::VideoCodec::kH264,
+                               .profile =
+                                   ::media::VideoCodecProfile::H264PROFILE_MAIN,
+                               .level = 40},
+       .mime = R"-(video/mp4; codecs="avc1.4D4028")-"});
 
   // h.264 high profile.
-  out.push_back({.codec_profile_level = {.codec = kCodecH264,
-                                         .profile = kH264High,
-                                         .level = 40},
-                 .mime = R"-(video/mp4; codecs="avc1.640028")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecH264,
-                                         .profile = kH264High,
-                                         .level = 41},
-                 .mime = R"-(video/mp4; codecs="avc1.640029")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecH264,
-                                         .profile = kH264High,
-                                         .level = 42},
-                 .mime = R"-(video/mp4; codecs="avc1.64002A")-"});
+  out.push_back(
+      {.codec_profile_level = {.codec = ::media::VideoCodec::kH264,
+                               .profile =
+                                   ::media::VideoCodecProfile::H264PROFILE_HIGH,
+                               .level = 40},
+       .mime = R"-(video/mp4; codecs="avc1.640028")-"});
+  out.push_back(
+      {.codec_profile_level = {.codec = ::media::VideoCodec::kH264,
+                               .profile =
+                                   ::media::VideoCodecProfile::H264PROFILE_HIGH,
+                               .level = 41},
+       .mime = R"-(video/mp4; codecs="avc1.640029")-"});
+  out.push_back(
+      {.codec_profile_level = {.codec = ::media::VideoCodec::kH264,
+                               .profile =
+                                   ::media::VideoCodecProfile::H264PROFILE_HIGH,
+                               .level = 42},
+       .mime = R"-(video/mp4; codecs="avc1.64002A")-"});
 
   // HEVC main profile.
-  out.push_back({.codec_profile_level = {.codec = kCodecHEVC,
-                                         .profile = kHEVCMain,
-                                         .level = 150},
-                 .mime = R"-(video/mp4; codecs="hev1.1.6.L150.B0")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecHEVC,
-                                         .profile = kHEVCMain,
-                                         .level = 153},
-                 .mime = R"-(video/mp4; codecs="hev1.1.6.L153.B0")-"});
+  out.push_back(
+      {.codec_profile_level = {.codec = ::media::VideoCodec::kHEVC,
+                               .profile =
+                                   ::media::VideoCodecProfile::HEVCPROFILE_MAIN,
+                               .level = 150},
+       .mime = R"-(video/mp4; codecs="hev1.1.6.L150.B0")-"});
+  out.push_back(
+      {.codec_profile_level = {.codec = ::media::VideoCodec::kHEVC,
+                               .profile =
+                                   ::media::VideoCodecProfile::HEVCPROFILE_MAIN,
+                               .level = 153},
+       .mime = R"-(video/mp4; codecs="hev1.1.6.L153.B0")-"});
 
   // HEVC main10 profile.
-  out.push_back({.codec_profile_level = {.codec = kCodecHEVC,
-                                         .profile = kHEVCMain10,
-                                         .level = 150},
+  out.push_back({.codec_profile_level =
+                     {.codec = ::media::VideoCodec::kHEVC,
+                      .profile = ::media::VideoCodecProfile::HEVCPROFILE_MAIN10,
+                      .level = 150},
                  .mime = R"-(video/mp4; codecs="hev1.2.6.L150.B0")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecHEVC,
-                                         .profile = kHEVCMain10,
-                                         .level = 153},
+  out.push_back({.codec_profile_level =
+                     {.codec = ::media::VideoCodec::kHEVC,
+                      .profile = ::media::VideoCodecProfile::HEVCPROFILE_MAIN10,
+                      .level = 153},
                  .mime = R"-(video/mp4; codecs="hev1.2.6.L153.B0")-"});
 
   // VP8.
-  out.push_back({.codec_profile_level = {.codec = kCodecVP8,
-                                         .profile = kVP8ProfileAny,
-                                         .level = 0},
-                 .mime = R"-(video/webm; codecs="vp8")-"});
+  out.push_back(
+      {.codec_profile_level = {.codec = ::media::VideoCodec::kVP8,
+                               .profile =
+                                   ::media::VideoCodecProfile::VP8PROFILE_ANY,
+                               .level = 0},
+       .mime = R"-(video/webm; codecs="vp8")-"});
 
   // VP9. Note that these assume bit depth 10, since the chromium code does not
   // include bit depth when checking decoder support for the codec.
-  out.push_back({.codec_profile_level = {.codec = kCodecVP9,
-                                         .profile = kVP9Profile0,
-                                         .level = 10},
-                 .mime = R"-(video/webm; codecs="vp09.00.10.10")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecVP9,
-                                         .profile = kVP9Profile1,
-                                         .level = 10},
-                 .mime = R"-(video/webm; codecs="vp09.01.10.10")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecVP9,
-                                         .profile = kVP9Profile2,
-                                         .level = 10},
-                 .mime = R"-(video/webm; codecs="vp09.02.10.10")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecVP9,
-                                         .profile = kVP9Profile3,
-                                         .level = 10},
-                 .mime = R"-(video/webm; codecs="vp09.03.10.10")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kVP9,
+            .profile = ::media::VideoCodecProfile::VP9PROFILE_PROFILE0,
+            .level = 10},
+       .mime = R"-(video/webm; codecs="vp09.00.10.10")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kVP9,
+            .profile = ::media::VideoCodecProfile::VP9PROFILE_PROFILE1,
+            .level = 10},
+       .mime = R"-(video/webm; codecs="vp09.01.10.10")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kVP9,
+            .profile = ::media::VideoCodecProfile::VP9PROFILE_PROFILE2,
+            .level = 10},
+       .mime = R"-(video/webm; codecs="vp09.02.10.10")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kVP9,
+            .profile = ::media::VideoCodecProfile::VP9PROFILE_PROFILE3,
+            .level = 10},
+       .mime = R"-(video/webm; codecs="vp09.03.10.10")-"});
 
   // Check VP9 with a different level. There are many supported levels, so we do
   // not include all possible values.
-  out.push_back({.codec_profile_level = {.codec = kCodecVP9,
-                                         .profile = kVP9Profile0,
-                                         .level = 62},
-                 .mime = R"-(video/webm; codecs="vp09.00.62.10")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecVP9,
-                                         .profile = kVP9Profile1,
-                                         .level = 62},
-                 .mime = R"-(video/webm; codecs="vp09.01.62.10")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecVP9,
-                                         .profile = kVP9Profile2,
-                                         .level = 62},
-                 .mime = R"-(video/webm; codecs="vp09.02.62.10")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecVP9,
-                                         .profile = kVP9Profile3,
-                                         .level = 62},
-                 .mime = R"-(video/webm; codecs="vp09.03.62.10")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kVP9,
+            .profile = ::media::VideoCodecProfile::VP9PROFILE_PROFILE0,
+            .level = 62},
+       .mime = R"-(video/webm; codecs="vp09.00.62.10")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kVP9,
+            .profile = ::media::VideoCodecProfile::VP9PROFILE_PROFILE1,
+            .level = 62},
+       .mime = R"-(video/webm; codecs="vp09.01.62.10")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kVP9,
+            .profile = ::media::VideoCodecProfile::VP9PROFILE_PROFILE2,
+            .level = 62},
+       .mime = R"-(video/webm; codecs="vp09.02.62.10")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kVP9,
+            .profile = ::media::VideoCodecProfile::VP9PROFILE_PROFILE3,
+            .level = 62},
+       .mime = R"-(video/webm; codecs="vp09.03.62.10")-"});
 
   // Dolby Vision profile 5.
-  out.push_back({.codec_profile_level = {.codec = kCodecDolbyVisionHEVC,
-                                         .profile = kDolbyVisionProfile5,
-                                         .level = 6},
-                 .mime = R"-(video/mp4; codecs="dvhe.05.06")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecDolbyVisionHEVC,
-                                         .profile = kDolbyVisionProfile5,
-                                         .level = 7},
-                 .mime = R"-(video/mp4; codecs="dvhe.05.07")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecDolbyVisionHEVC,
-                                         .profile = kDolbyVisionProfile5,
-                                         .level = 9},
-                 .mime = R"-(video/mp4; codecs="dvhe.05.09")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kDolbyVision,
+            .profile = ::media::VideoCodecProfile::DOLBYVISION_PROFILE5,
+            .level = 6},
+       .mime = R"-(video/mp4; codecs="dvhe.05.06")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kDolbyVision,
+            .profile = ::media::VideoCodecProfile::DOLBYVISION_PROFILE5,
+            .level = 7},
+       .mime = R"-(video/mp4; codecs="dvhe.05.07")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kDolbyVision,
+            .profile = ::media::VideoCodecProfile::DOLBYVISION_PROFILE5,
+            .level = 9},
+       .mime = R"-(video/mp4; codecs="dvhe.05.09")-"});
 
   // Dolby Vision profile 8.
-  out.push_back({.codec_profile_level = {.codec = kCodecDolbyVisionHEVC,
-                                         .profile = kDolbyVisionProfile8,
-                                         .level = 6},
-                 .mime = R"-(video/mp4; codecs="dvhe.08.06")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecDolbyVisionHEVC,
-                                         .profile = kDolbyVisionProfile8,
-                                         .level = 7},
-                 .mime = R"-(video/mp4; codecs="dvhe.08.07")-"});
-  out.push_back({.codec_profile_level = {.codec = kCodecDolbyVisionHEVC,
-                                         .profile = kDolbyVisionProfile8,
-                                         .level = 9},
-                 .mime = R"-(video/mp4; codecs="dvhe.08.09")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kDolbyVision,
+            .profile = ::media::VideoCodecProfile::DOLBYVISION_PROFILE8,
+            .level = 6},
+       .mime = R"-(video/mp4; codecs="dvhe.08.06")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kDolbyVision,
+            .profile = ::media::VideoCodecProfile::DOLBYVISION_PROFILE8,
+            .level = 7},
+       .mime = R"-(video/mp4; codecs="dvhe.08.07")-"});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kDolbyVision,
+            .profile = ::media::VideoCodecProfile::DOLBYVISION_PROFILE8,
+            .level = 9},
+       .mime = R"-(video/mp4; codecs="dvhe.08.09")-"});
 
   // Unsupported cases should return an empty string for the MIME type.
-  out.push_back({.codec_profile_level = {.codec = kCodecDolbyVisionHEVC,
-                                         .profile = kDolbyVisionProfile5,
-                                         .level = 11},
-                 .mime = ""});
-  out.push_back({.codec_profile_level = {.codec = kCodecDolbyVisionHEVC,
-                                         .profile = kDolbyVisionProfile8,
-                                         .level = 11},
-                 .mime = ""});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kDolbyVision,
+            .profile = ::media::VideoCodecProfile::DOLBYVISION_PROFILE5,
+            .level = 11},
+       .mime = ""});
+  out.push_back(
+      {.codec_profile_level =
+           {.codec = ::media::VideoCodec::kDolbyVision,
+            .profile = ::media::VideoCodecProfile::DOLBYVISION_PROFILE8,
+            .level = 11},
+       .mime = ""});
 
   // Invalid codec/profile/level combinations should return an empty string.
 
   // Mixed h.264 profile with HEVC codec.
-  out.push_back({.codec_profile_level = {.codec = kCodecHEVC,
-                                         .profile = kH264Main,
-                                         .level = 150},
-                 .mime = ""});
+  out.push_back(
+      {.codec_profile_level = {.codec = ::media::VideoCodec::kHEVC,
+                               .profile =
+                                   ::media::VideoCodecProfile::H264PROFILE_MAIN,
+                               .level = 150},
+       .mime = ""});
 
-  // Mixed VP9 with HEVC profile.
-  out.push_back({.codec_profile_level = {.codec = kCodecVP9,
-                                         .profile = kH264Main,
-                                         .level = 62},
-                 .mime = ""});
+  // Mixed VP9 with h.264 profile.
+  out.push_back(
+      {.codec_profile_level = {.codec = ::media::VideoCodec::kVP9,
+                               .profile =
+                                   ::media::VideoCodecProfile::H264PROFILE_MAIN,
+                               .level = 62},
+       .mime = ""});
 
   return out;
 }
@@ -195,40 +255,44 @@
   std::vector<AudioCodecInfoAndMime> out;
 
   // AAC.
-  out.push_back(
-      {.codec = kCodecAAC, .mime = R"-(audio/mp4; codecs="mp4a.40.5")-"});
+  out.push_back({.codec = ::media::AudioCodec::kAAC,
+                 .mime = R"-(audio/mp4; codecs="mp4a.40.5")-"});
 
   // MP3
-  out.push_back(
-      {.codec = kCodecMP3, .mime = R"-(audio/mp4; codecs="mp4a.69")-"});
+  out.push_back({.codec = ::media::AudioCodec::kMP3,
+                 .mime = R"-(audio/mp4; codecs="mp4a.69")-"});
 
   // PCM.
-  out.push_back({.codec = kCodecPCM, .mime = R"-(audio/wav; codecs="1")-"});
-  out.push_back(
-      {.codec = kCodecPCM_S16BE, .mime = R"-(audio/wav; codecs="1")-"});
+  out.push_back({.codec = ::media::AudioCodec::kPCM,
+                 .mime = R"-(audio/wav; codecs="1")-"});
+  out.push_back({.codec = ::media::AudioCodec::kPCM_S16BE,
+                 .mime = R"-(audio/wav; codecs="1")-"});
 
   // Vorbis.
-  out.push_back(
-      {.codec = kCodecVorbis, .mime = R"-(audio/webm; codecs="vorbis")-"});
+  out.push_back({.codec = ::media::AudioCodec::kVorbis,
+                 .mime = R"-(audio/webm; codecs="vorbis")-"});
 
   // Opus.
-  out.push_back(
-      {.codec = kCodecOpus, .mime = R"-(audio/webm; codecs="opus")-"});
+  out.push_back({.codec = ::media::AudioCodec::kOpus,
+                 .mime = R"-(audio/webm; codecs="opus")-"});
 
   // E-AC-3.
-  out.push_back({.codec = kCodecEAC3, .mime = R"-(audio/mp4; codecs="ec-3")-"});
+  out.push_back({.codec = ::media::AudioCodec::kEAC3,
+                 .mime = R"-(audio/mp4; codecs="ec-3")-"});
 
   // AC-3.
-  out.push_back({.codec = kCodecAC3, .mime = R"-(audio/mp4; codecs="ac-3")-"});
+  out.push_back({.codec = ::media::AudioCodec::kAC3,
+                 .mime = R"-(audio/mp4; codecs="ac-3")-"});
 
   // FLAC.
-  out.push_back({.codec = kCodecFLAC, .mime = R"-(audio/ogg; codecs="flac")-"});
+  out.push_back({.codec = ::media::AudioCodec::kFLAC,
+                 .mime = R"-(audio/ogg; codecs="flac")-"});
 
   // Unsupported codecs should return an empty string.
-  out.push_back({.codec = kCodecDTS, .mime = ""});
-  out.push_back({.codec = kCodecMpegHAudio, .mime = ""});
-  out.push_back({.codec = kCodecDTSXP2, .mime = ""});
-  out.push_back({.codec = kCodecDTSE, .mime = ""});
+  out.push_back({.codec = ::media::AudioCodec::kDTS, .mime = ""});
+  out.push_back({.codec = ::media::AudioCodec::kMpegHAudio, .mime = ""});
+  out.push_back({.codec = ::media::AudioCodec::kDTSXP2, .mime = ""});
+  out.push_back({.codec = ::media::AudioCodec::kDTSE, .mime = ""});
 
   return out;
 }
@@ -237,13 +301,22 @@
 using MimeUtilsVideoCodecTest = ::testing::TestWithParam<VideoCodecInfoAndMime>;
 
 TEST_P(MimeUtilsVideoCodecTest, ConvertsToMimeType) {
-  const CodecProfileLevel codec_profile_level = GetParam().codec_profile_level;
+  const ::media::CodecProfileLevel codec_profile_level =
+      GetParam().codec_profile_level;
   const std::string expected_mime = GetParam().mime;
 
+  // Check the function that takes chromium enums.
   EXPECT_THAT(
       GetMimeType(codec_profile_level.codec, codec_profile_level.profile,
                   codec_profile_level.level),
       StrEq(expected_mime));
+
+  // Check the function that takes cast enums.
+  EXPECT_THAT(GetMimeType(ToCastVideoCodec(codec_profile_level.codec,
+                                           codec_profile_level.profile),
+                          ToCastVideoProfile(codec_profile_level.profile),
+                          static_cast<int32_t>(codec_profile_level.level)),
+              StrEq(expected_mime));
 }
 
 INSTANTIATE_TEST_SUITE_P(VideoMimeTypes,
@@ -254,7 +327,12 @@
 using MimeUtilsAudioCodecTest = ::testing::TestWithParam<AudioCodecInfoAndMime>;
 
 TEST_P(MimeUtilsAudioCodecTest, ConvertsToMimeType) {
+  // Check the function that takes ::media::AudioCodec.
   EXPECT_THAT(GetMimeType(GetParam().codec), StrEq(GetParam().mime));
+
+  // Check the function that takes ::chromecast::media::AudioCodec.
+  EXPECT_THAT(GetMimeType(ToCastAudioCodec(GetParam().codec)),
+              StrEq(GetParam().mime));
 }
 
 INSTANTIATE_TEST_SUITE_P(AudioMimeTypes,
diff --git a/chromecast/starboard/media/media/starboard_resampler.cc b/chromecast/starboard/media/media/starboard_resampler.cc
index 42474d7..e0b364cb 100644
--- a/chromecast/starboard/media/media/starboard_resampler.cc
+++ b/chromecast/starboard/media/media/starboard_resampler.cc
@@ -133,6 +133,8 @@
     case kStarboardPcmSampleFormatS32:
     case kStarboardPcmSampleFormatF32:
       return 4;
+    default:
+      LOG(FATAL) << "Unsupported StarboardPcmSampleFormat: " << format;
   }
 }
 
@@ -267,6 +269,71 @@
        format_to_decode_from == kSampleFormatF32);
   return !same_format;
 }
+
+// Converts from chromium SampleFormat to cast SampleFormat.
+//
+// TODO(crbug.com/323610278): remove this after deprecating CMA.
+SampleFormat ToCastSampleFormat(::media::SampleFormat format) {
+  switch (format) {
+    case ::media::kSampleFormatU8:
+      return kSampleFormatU8;
+    case ::media::kSampleFormatS16:
+      return kSampleFormatS16;
+    case ::media::kSampleFormatS24:
+      return kSampleFormatS24;
+    case ::media::kSampleFormatS32:
+      return kSampleFormatS32;
+    case ::media::kSampleFormatF32:
+      return kSampleFormatF32;
+    case ::media::kSampleFormatPlanarU8:
+      return kSampleFormatPlanarU8;
+    case ::media::kSampleFormatPlanarS16:
+      return kSampleFormatPlanarS16;
+    case ::media::kSampleFormatPlanarF32:
+      return kSampleFormatPlanarF32;
+    case ::media::kSampleFormatPlanarS32:
+      return kSampleFormatPlanarS32;
+    default:
+      LOG(FATAL) << "Unsupported ::media::SampleFormat: " << format;
+  }
+}
+
+// Converts from chromium AudioCodec to cast AudioCodec.
+//
+// TODO(crbug.com/323610278): remove this after deprecating CMA.
+AudioCodec ToCastAudioCodec(::media::AudioCodec codec) {
+  switch (codec) {
+    case ::media::AudioCodec::kAAC:
+      return kCodecAAC;
+    case ::media::AudioCodec::kMP3:
+      return kCodecMP3;
+    case ::media::AudioCodec::kPCM:
+      return kCodecPCM;
+    case ::media::AudioCodec::kPCM_S16BE:
+      return kCodecPCM_S16BE;
+    case ::media::AudioCodec::kVorbis:
+      return kCodecVorbis;
+    case ::media::AudioCodec::kOpus:
+      return kCodecOpus;
+    case ::media::AudioCodec::kFLAC:
+      return kCodecFLAC;
+    case ::media::AudioCodec::kEAC3:
+      return kCodecEAC3;
+    case ::media::AudioCodec::kAC3:
+      return kCodecAC3;
+    case ::media::AudioCodec::kMpegHAudio:
+      return kCodecMpegHAudio;
+    case ::media::AudioCodec::kDTS:
+      return kCodecDTS;
+    case ::media::AudioCodec::kDTSXP2:
+      return kCodecDTSXP2;
+    case ::media::AudioCodec::kDTSE:
+      return kCodecDTSE;
+    default:
+      LOG(FATAL) << "Unsupported audio codec: " << codec;
+  }
+}
+
 }  // namespace
 
 base::HeapArray<uint8_t> ResamplePCMAudioDataForStarboard(
@@ -290,5 +357,16 @@
                      format_to_decode_to, audio_codec);
 }
 
+base::HeapArray<uint8_t> ResamplePCMAudioDataForStarboard(
+    StarboardPcmSampleFormat format_to_decode_to,
+    ::media::SampleFormat format_to_decode_from,
+    ::media::AudioCodec audio_codec,
+    int audio_channels,
+    base::span<const uint8_t> in_data) {
+  return ResamplePCMAudioDataForStarboard(
+      format_to_decode_to, ToCastSampleFormat(format_to_decode_from),
+      ToCastAudioCodec(audio_codec), audio_channels, in_data);
+}
+
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/starboard/media/media/starboard_resampler.h b/chromecast/starboard/media/media/starboard_resampler.h
index 50f2c09..80a9c1b 100644
--- a/chromecast/starboard/media/media/starboard_resampler.h
+++ b/chromecast/starboard/media/media/starboard_resampler.h
@@ -12,6 +12,8 @@
 #include "chromecast/public/media/cast_decoder_buffer.h"
 #include "chromecast/public/media/decoder_config.h"
 #include "chromecast/starboard/chromecast/starboard_cast_api/cast_starboard_api_types.h"
+#include "media/base/audio_codecs.h"
+#include "media/base/sample_format.h"
 
 namespace chromecast {
 namespace media {
@@ -22,8 +24,7 @@
 // kCodecPCM_S16BE. `audio_channels` is the number of channels in the input
 // data, and must be greater than 0. `in_data` contains the input PCM data.
 //
-// TODO(b/334991778): see if we can reuse existing chromium infra to do the
-// conversion.
+// TODO: crbug.com/323610278 - remove this when cast no longer uses CMA.
 base::HeapArray<uint8_t> ResamplePCMAudioDataForStarboard(
     StarboardPcmSampleFormat format_to_decode_to,
     SampleFormat format_to_decode_from,
@@ -31,6 +32,14 @@
     int audio_channels,
     base::span<const uint8_t> in_data);
 
+// Same as above, but uses chromium enums instead of cast ones.
+base::HeapArray<uint8_t> ResamplePCMAudioDataForStarboard(
+    StarboardPcmSampleFormat format_to_decode_to,
+    ::media::SampleFormat format_to_decode_from,
+    ::media::AudioCodec audio_codec,
+    int audio_channels,
+    base::span<const uint8_t> in_data);
+
 }  // namespace media
 }  // namespace chromecast
 
diff --git a/chromecast/starboard/media/media/starboard_resampler_test.cc b/chromecast/starboard/media/media/starboard_resampler_test.cc
index 624f7324..446a6bd 100644
--- a/chromecast/starboard/media/media/starboard_resampler_test.cc
+++ b/chromecast/starboard/media/media/starboard_resampler_test.cc
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// TODO: crbug.com/323610278 - Remove the "Cast enum version" calls in each test
+// once linux cast builds no longer use CMA.
+
 #include "chromecast/starboard/media/media/starboard_resampler.h"
 
 #include <cstdint>
@@ -49,11 +52,20 @@
   const std::vector<int16_t> expected_data = {static_cast<int16_t>(0x8000), 0,
                                               0x7FFF};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatS16,
                                                kSampleFormatU8, kCodecPCM, 2,
                                                buffer_data)
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS16, ::media::SampleFormat::kSampleFormatU8,
+          ::media::AudioCodec::kPCM, 2, buffer_data)
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PCM8ToS32) {
@@ -61,11 +73,20 @@
   const std::vector<int32_t> expected_data = {static_cast<int32_t>(0x80000000),
                                               0, 0x7FFFFFFF};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatS32,
                                                kSampleFormatU8, kCodecPCM, 2,
                                                buffer_data)
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS32, ::media::SampleFormat::kSampleFormatU8,
+          ::media::AudioCodec::kPCM, 2, buffer_data)
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PCMU8ToFloat) {
@@ -73,10 +94,18 @@
   const std::vector<float> expected_f32_data = {-0.9921875, -0.984375,
                                                 -0.9765625, -0.96875};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatF32,
                                                kSampleFormatU8, kCodecPCM, 2,
                                                buffer_data),
               MatchesFloatSpan(expected_f32_data));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatF32, ::media::SampleFormat::kSampleFormatU8,
+          ::media::AudioCodec::kPCM, 2, buffer_data),
+      MatchesFloatSpan(expected_f32_data));
 }
 
 ////// END OF U8 TO OTHER FORMATS
@@ -86,11 +115,20 @@
   const std::vector<int16_t> buffer_data = {-32768, 0, 32767};
   const std::vector<int16_t> expected_data = {-32768, 0, 32767};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatS16,
                                                kSampleFormatS16, kCodecPCM, 2,
                                                base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS16, ::media::SampleFormat::kSampleFormatS16,
+          ::media::AudioCodec::kPCM, 2, base::as_byte_span(buffer_data))
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PCMS16ToS32) {
@@ -99,11 +137,20 @@
   const std::vector<int32_t> expected_data = {static_cast<int32_t>(0x80000000),
                                               0, 0x7FFFFFFF};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatS32,
                                                kSampleFormatS16, kCodecPCM, 2,
                                                base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS32, ::media::SampleFormat::kSampleFormatS16,
+          ::media::AudioCodec::kPCM, 2, base::as_byte_span(buffer_data))
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PCMS16ToFloat) {
@@ -113,10 +160,18 @@
   const std::vector<float> expected_f32_data = {-1.0f, -0.5f, 0.0f,
                                                 0.499984741f, 1.0f};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatF32,
                                                kSampleFormatS16, kCodecPCM, 2,
                                                base::as_byte_span(buffer_data)),
               MatchesFloatSpan(expected_f32_data));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatF32, ::media::SampleFormat::kSampleFormatS16,
+          ::media::AudioCodec::kPCM, 2, base::as_byte_span(buffer_data)),
+      MatchesFloatSpan(expected_f32_data));
 }
 
 // End of Signed 16 Conversions
@@ -127,11 +182,20 @@
                                             0,    0xFF, 0xFF, 0x7F};
   const std::vector<int16_t> expected_data = {-17493, 0, 32767};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatS16,
                                                kSampleFormatS24, kCodecPCM, 2,
                                                base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS16, ::media::SampleFormat::kSampleFormatS24,
+          ::media::AudioCodec::kPCM, 2, base::as_byte_span(buffer_data))
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PCMS24ToS32) {
@@ -142,11 +206,20 @@
   const std::vector<int32_t> expected_data = {static_cast<int32_t>(0x80000000),
                                               0, 0x7FFFFFFF};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatS32,
                                                kSampleFormatS24, kCodecPCM, 2,
                                                base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS32, ::media::SampleFormat::kSampleFormatS24,
+          ::media::AudioCodec::kPCM, 2, base::as_byte_span(buffer_data))
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PCMS24ToFloat) {
@@ -154,10 +227,18 @@
                                             0, 0xFF, 0xFF, 0x7F};
   const std::vector<float> expected_f32_data = {-1.0f, 0.0f, 1.0f};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatF32,
                                                kSampleFormatS24, kCodecPCM, 2,
                                                base::as_byte_span(buffer_data)),
               MatchesFloatSpan(expected_f32_data));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatF32, ::media::SampleFormat::kSampleFormatS24,
+          ::media::AudioCodec::kPCM, 2, base::as_byte_span(buffer_data)),
+      MatchesFloatSpan(expected_f32_data));
 }
 
 // End of Signed 24 Conversions
@@ -167,22 +248,40 @@
   const std::vector<int32_t> buffer_data = {-2147483648, 0, 70000, 2147483647};
   const std::vector<int16_t> expected_data = {-32768, 0, 1, 32767};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatS16,
                                                kSampleFormatS32, kCodecPCM, 2,
                                                base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS16, ::media::SampleFormat::kSampleFormatS32,
+          ::media::AudioCodec::kPCM, 2, base::as_byte_span(buffer_data))
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PCMS32ToS32) {
   const std::vector<int32_t> buffer_data = {-2147483647, 0, 2147483647};
   const std::vector<int32_t> expected_data = {-2147483647, 0, 2147483647};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatS32,
                                                kSampleFormatS32, kCodecPCM, 2,
                                                base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS32, ::media::SampleFormat::kSampleFormatS32,
+          ::media::AudioCodec::kPCM, 2, base::as_byte_span(buffer_data))
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PCMS32ToFloat) {
@@ -190,10 +289,18 @@
                                             0x7FFFFFFF};
   const std::vector<float> expected_f32_data = {-1.0f, 0.0f, 1.0f};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(kStarboardPcmSampleFormatF32,
                                                kSampleFormatS32, kCodecPCM, 2,
                                                base::as_byte_span(buffer_data)),
               MatchesFloatSpan(expected_f32_data));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatF32, ::media::SampleFormat::kSampleFormatS32,
+          ::media::AudioCodec::kPCM, 2, base::as_byte_span(buffer_data)),
+      MatchesFloatSpan(expected_f32_data));
 }
 
 // End of Signed 32 Conversions
@@ -203,32 +310,61 @@
   const std::vector<float> buffer_data = {-1.0f, 0.0f, 1.0f};
   const std::vector<int16_t> expected_data = {-32768, 0, 32767};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(
                   kStarboardPcmSampleFormatS16, kSampleFormatF32, kCodecPCM, 2,
                   base::as_byte_span(base::allow_nonunique_obj, buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS16, ::media::SampleFormat::kSampleFormatF32,
+          ::media::AudioCodec::kPCM, 2,
+          base::as_byte_span(base::allow_nonunique_obj, buffer_data))
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PCMFloatToS32) {
   const std::vector<float> buffer_data = {-1.0f, 0.0f, 1.0f};
   const std::vector<int32_t> expected_data = {-2147483648, 0, 2147483647};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(
                   kStarboardPcmSampleFormatS32, kSampleFormatF32, kCodecPCM, 2,
                   base::as_byte_span(base::allow_nonunique_obj, buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS32, ::media::SampleFormat::kSampleFormatF32,
+          ::media::AudioCodec::kPCM, 2,
+          base::as_byte_span(base::allow_nonunique_obj, buffer_data))
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PCMFloatToFloat) {
   const std::vector<float> buffer_data = {-1, 0.0234375, 0, 1};
   const std::vector<float> expected_f32_data = {-1, 0.0234375, 0, 1};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(
                   kStarboardPcmSampleFormatF32, kSampleFormatF32, kCodecPCM, 2,
                   base::as_byte_span(base::allow_nonunique_obj, buffer_data)),
               MatchesFloatSpan(expected_f32_data));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatF32, ::media::SampleFormat::kSampleFormatF32,
+          ::media::AudioCodec::kPCM, 2,
+          base::as_byte_span(base::allow_nonunique_obj, buffer_data)),
+      MatchesFloatSpan(expected_f32_data));
 }
 
 // End of Float Conversions
@@ -240,11 +376,20 @@
   const std::vector<int16_t> expected_data = {-32768, 16384, 32767,
                                               -32768, 16384, 32767};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(
                   kStarboardPcmSampleFormatS16, kSampleFormatPlanarS16,
                   kCodecPCM, 3, base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(ResamplePCMAudioDataForStarboard(
+                  kStarboardPcmSampleFormatS16,
+                  ::media::SampleFormat::kSampleFormatPlanarS16,
+                  ::media::AudioCodec::kPCM, 3, base::as_byte_span(buffer_data))
+                  .as_span(),
+              ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PushesBufferToStarboardPlanarPCM32) {
@@ -254,11 +399,20 @@
   const std::vector<int16_t> expected_data = {-32768, 15, 31, 32767,
                                               -32768, 15, 31, 32767};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(
                   kStarboardPcmSampleFormatS16, kSampleFormatPlanarS32,
                   kCodecPCM, 4, base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(ResamplePCMAudioDataForStarboard(
+                  kStarboardPcmSampleFormatS16,
+                  ::media::SampleFormat::kSampleFormatPlanarS32,
+                  ::media::AudioCodec::kPCM, 4, base::as_byte_span(buffer_data))
+                  .as_span(),
+              ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PushesBufferToStarboardPlanarPCMF32) {
@@ -267,12 +421,22 @@
   const std::vector<int16_t> expected_data = {-32768, 15, 32767,
                                               -32768, 15, 32767};
 
+  // Cast enum version.
   EXPECT_THAT(
       ResamplePCMAudioDataForStarboard(
           kStarboardPcmSampleFormatS16, kSampleFormatPlanarF32, kCodecPCM, 3,
           base::as_byte_span(base::allow_nonunique_obj, buffer_data))
           .as_span(),
       ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(ResamplePCMAudioDataForStarboard(
+                  kStarboardPcmSampleFormatS16,
+                  ::media::SampleFormat::kSampleFormatPlanarF32,
+                  ::media::AudioCodec::kPCM, 3,
+                  base::as_byte_span(base::allow_nonunique_obj, buffer_data))
+                  .as_span(),
+              ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PushesBufferToStarboardPlanarPCM32Mono) {
@@ -300,11 +464,20 @@
                                               0x900,
                                               0x7FFF};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(
                   kStarboardPcmSampleFormatS16, kSampleFormatPlanarS32,
                   kCodecPCM, 1, base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(ResamplePCMAudioDataForStarboard(
+                  kStarboardPcmSampleFormatS16,
+                  ::media::SampleFormat::kSampleFormatPlanarS32,
+                  ::media::AudioCodec::kPCM, 1, base::as_byte_span(buffer_data))
+                  .as_span(),
+              ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest, PushesBufferToStarboardPlanarPCM32MaxChannels) {
@@ -316,11 +489,20 @@
       0x0, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700,
       0x0, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(
                   kStarboardPcmSampleFormatS16, kSampleFormatPlanarS32,
                   kCodecPCM, 8, base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(ResamplePCMAudioDataForStarboard(
+                  kStarboardPcmSampleFormatS16,
+                  ::media::SampleFormat::kSampleFormatPlanarS32,
+                  ::media::AudioCodec::kPCM, 8, base::as_byte_span(buffer_data))
+                  .as_span(),
+              ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 TEST(StarboardResamplerTest,
@@ -328,11 +510,20 @@
   const std::vector<int16_t> buffer_data = {32640, 1040, 4100, 255};
   const std::vector<int16_t> expected_data = {-32641, 4100, 1040, -256};
 
+  // Cast enum version.
   EXPECT_THAT(ResamplePCMAudioDataForStarboard(
                   kStarboardPcmSampleFormatS16, kSampleFormatS16,
                   kCodecPCM_S16BE, 2, base::as_byte_span(buffer_data))
                   .as_span(),
               ElementsAreArray(base::as_byte_span(expected_data)));
+
+  // Chromium enum version.
+  EXPECT_THAT(
+      ResamplePCMAudioDataForStarboard(
+          kStarboardPcmSampleFormatS16, ::media::SampleFormat::kSampleFormatS16,
+          ::media::AudioCodec::kPCM_S16BE, 2, base::as_byte_span(buffer_data))
+          .as_span(),
+      ElementsAreArray(base::as_byte_span(expected_data)));
 }
 
 // End of Signed 16 Conversions
diff --git a/chromecast/starboard/media/media/test_matchers.cc b/chromecast/starboard/media/media/test_matchers.cc
new file mode 100644
index 0000000..f5a0eb2
--- /dev/null
+++ b/chromecast/starboard/media/media/test_matchers.cc
@@ -0,0 +1,282 @@
+// 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 "chromecast/starboard/media/media/test_matchers.h"
+
+#include <cstdint>
+#include <tuple>
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+using ::testing::AllOf;
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::ExplainMatchResult;
+using ::testing::Field;
+using ::testing::FloatEq;
+using ::testing::IsNull;
+using ::testing::Matcher;
+using ::testing::Pointee;
+using ::testing::Pointwise;
+using ::testing::StrEq;
+
+// Takes an arg that can be converted to a span, and matches the first
+// expected.size() values to expected.
+MATCHER_P(SpanMatches, expected_span, "") {
+  base::span<const uint8_t> actual_span = arg;
+  if (actual_span.size() < expected_span.size()) {
+    *result_listener << "too few elements (expected at least "
+                     << expected_span.size() << ", got " << actual_span.size()
+                     << ")";
+    return false;
+  }
+  return ExplainMatchResult(ElementsAreArray(expected_span),
+                            actual_span.first(expected_span.size()),
+                            result_listener);
+}
+
+auto MatchesSubsampleMapping(const StarboardDrmSubSampleMapping& expected) {
+  return AllOf(
+      Field("clear_byte_count", &StarboardDrmSubSampleMapping::clear_byte_count,
+            Eq(expected.clear_byte_count)),
+      Field("encrypted_byte_count",
+            &StarboardDrmSubSampleMapping::encrypted_byte_count,
+            Eq(expected.encrypted_byte_count)));
+}
+
+// Used as an argument to Pointwise. Takes a tuple of
+// StarboardDrmSubSampleMapping values and returns whether they are equal
+// according to MatchesSubsampleMapping.
+MATCHER(MatchesSubsampleMappingTuple, "") {
+  const StarboardDrmSubSampleMapping left = std::get<0>(arg);
+  const StarboardDrmSubSampleMapping right = std::get<1>(arg);
+  return ExplainMatchResult(MatchesSubsampleMapping(right), left,
+                            result_listener);
+}
+
+auto MatchesSideData(const StarboardSampleSideData& expected) {
+  return AllOf(Field("type", &StarboardSampleSideData::type, Eq(expected.type)),
+               Field("data", &StarboardSampleSideData::data,
+                     ElementsAreArray(expected.data)));
+}
+
+// Used as an argument to Pointwise. Takes a tuple of StarboardSampleSideData
+// values and returns whether they are equal according to MatchesSideData.
+MATCHER(MatchesSideDataTuple, "") {
+  const StarboardSampleSideData left = std::get<0>(arg);
+  const StarboardSampleSideData right = std::get<1>(arg);
+  return left.type == right.type &&
+         ExplainMatchResult(MatchesSideData(right), left, result_listener);
+}
+
+auto MatchesMasteringMetadata(const StarboardMediaMasteringMetadata& expected) {
+  return AllOf(
+      Field("primary_r_chromaticity_x",
+            &StarboardMediaMasteringMetadata::primary_r_chromaticity_x,
+            FloatEq(expected.primary_r_chromaticity_x)),
+      Field("primary_r_chromaticity_y",
+            &StarboardMediaMasteringMetadata::primary_r_chromaticity_y,
+            FloatEq(expected.primary_r_chromaticity_y)),
+      Field("primary_g_chromaticity_x",
+            &StarboardMediaMasteringMetadata::primary_g_chromaticity_x,
+            FloatEq(expected.primary_g_chromaticity_x)),
+      Field("primary_g_chromaticity_y",
+            &StarboardMediaMasteringMetadata::primary_g_chromaticity_y,
+            FloatEq(expected.primary_g_chromaticity_y)),
+      Field("primary_b_chromaticity_x",
+            &StarboardMediaMasteringMetadata::primary_b_chromaticity_x,
+            FloatEq(expected.primary_b_chromaticity_x)),
+      Field("primary_b_chromaticity_y",
+            &StarboardMediaMasteringMetadata::primary_b_chromaticity_y,
+            FloatEq(expected.primary_b_chromaticity_y)),
+      Field("white_point_chromaticity_x",
+            &StarboardMediaMasteringMetadata::white_point_chromaticity_x,
+            FloatEq(expected.white_point_chromaticity_x)),
+      Field("white_point_chromaticity_y",
+            &StarboardMediaMasteringMetadata::white_point_chromaticity_y,
+            FloatEq(expected.white_point_chromaticity_y)),
+      Field("luminance_max", &StarboardMediaMasteringMetadata::luminance_max,
+            FloatEq(expected.luminance_max)),
+      Field("luminance_min", &StarboardMediaMasteringMetadata::luminance_min,
+            FloatEq(expected.luminance_min)));
+}
+
+auto MatchesColorMetadata(const StarboardColorMetadata& expected) {
+  return AllOf(
+      Field("bits_per_channel", &StarboardColorMetadata::bits_per_channel,
+            Eq(expected.bits_per_channel)),
+      Field("chroma_subsampling_horizontal",
+            &StarboardColorMetadata::chroma_subsampling_horizontal,
+            Eq(expected.chroma_subsampling_horizontal)),
+      Field("chroma_subsampling_vertical",
+            &StarboardColorMetadata::chroma_subsampling_vertical,
+            Eq(expected.chroma_subsampling_vertical)),
+      Field("cb_subsampling_horizontal",
+            &StarboardColorMetadata::cb_subsampling_horizontal,
+            Eq(expected.cb_subsampling_horizontal)),
+      Field("cb_subsampling_vertical",
+            &StarboardColorMetadata::cb_subsampling_vertical,
+            Eq(expected.cb_subsampling_vertical)),
+      Field("chroma_siting_horizontal",
+            &StarboardColorMetadata::chroma_siting_horizontal,
+            Eq(expected.chroma_siting_horizontal)),
+      Field("chroma_siting_vertical",
+            &StarboardColorMetadata::chroma_siting_vertical,
+            Eq(expected.chroma_siting_vertical)),
+      Field("mastering_metadata", &StarboardColorMetadata::mastering_metadata,
+            MatchesMasteringMetadata(expected.mastering_metadata)),
+      Field("max_cll", &StarboardColorMetadata::max_cll, Eq(expected.max_cll)),
+      Field("max_fall", &StarboardColorMetadata::max_fall,
+            Eq(expected.max_fall)),
+      Field("primaries", &StarboardColorMetadata::primaries,
+            Eq(expected.primaries)),
+      Field("transfer", &StarboardColorMetadata::transfer,
+            Eq(expected.transfer)),
+      Field("matrix", &StarboardColorMetadata::matrix, Eq(expected.matrix)),
+      Field("range", &StarboardColorMetadata::range, Eq(expected.range)),
+      Field("custom_primary_matrix",
+            &StarboardColorMetadata::custom_primary_matrix,
+            Pointwise(FloatEq(), expected.custom_primary_matrix)));
+}
+
+}  // namespace
+
+Matcher<StarboardSampleInfo> MatchesStarboardSampleInfo(
+    const StarboardSampleInfo& expected) {
+  auto common_checks =
+      AllOf(Field("type", &StarboardSampleInfo::type, Eq(expected.type)),
+            Field("buffer", &StarboardSampleInfo::buffer, Eq(expected.buffer)),
+            Field("buffer_size", &StarboardSampleInfo::buffer_size,
+                  Eq(expected.buffer_size)),
+            Field("timestamp", &StarboardSampleInfo::timestamp,
+                  Eq(expected.timestamp)),
+            Field("side_data", &StarboardSampleInfo::side_data,
+                  Pointwise(MatchesSideDataTuple(), expected.side_data)),
+            expected.drm_info == nullptr
+                ? Field("drm_info", &StarboardSampleInfo::drm_info, IsNull())
+                : Field("drm_info", &StarboardSampleInfo::drm_info,
+                        Pointee(MatchesDrmInfo(*expected.drm_info))));
+
+  // Note that AllOf short circuits, so if there is a mismatch on `type` we
+  // avoid reading the audio_sample_info/video_sample_info union of the object
+  // being matched.
+  if (expected.type == 0) {
+    // Audio sample.
+    return AllOf(
+        common_checks,
+        Field("audio_sample_info", &StarboardSampleInfo::audio_sample_info,
+              MatchesAudioSampleInfo(expected.audio_sample_info)));
+  } else {
+    // Video sample.
+    return AllOf(
+        common_checks,
+        Field("video_sample_info", &StarboardSampleInfo::video_sample_info,
+              MatchesVideoSampleInfo(expected.video_sample_info)));
+  }
+}
+
+Matcher<StarboardAudioSampleInfo> MatchesAudioSampleInfo(
+    const StarboardAudioSampleInfo& expected) {
+  return AllOf(
+      Field("codec", &StarboardAudioSampleInfo::codec, Eq(expected.codec)),
+      expected.mime == nullptr
+          ? Field("mime", &StarboardAudioSampleInfo::mime, IsNull())
+          : Field("mime", &StarboardAudioSampleInfo::mime,
+                  StrEq(expected.mime)),
+      Field("format_tag", &StarboardAudioSampleInfo::format_tag,
+            Eq(expected.format_tag)),
+      Field("number_of_channels", &StarboardAudioSampleInfo::number_of_channels,
+            Eq(expected.number_of_channels)),
+      Field("samples_per_second", &StarboardAudioSampleInfo::samples_per_second,
+            Eq(expected.samples_per_second)),
+      Field("average_bytes_per_second",
+            &StarboardAudioSampleInfo::average_bytes_per_second,
+            Eq(expected.average_bytes_per_second)),
+      Field("block_alignment", &StarboardAudioSampleInfo::block_alignment,
+            Eq(expected.block_alignment)),
+      Field("bits_per_sample", &StarboardAudioSampleInfo::bits_per_sample,
+            Eq(expected.bits_per_sample)),
+      Field("audio_specific_config",
+            &StarboardAudioSampleInfo::audio_specific_config,
+            Eq(expected.audio_specific_config)),
+      Field("audio_specific_config",
+            &StarboardAudioSampleInfo::audio_specific_config,
+            Eq(expected.audio_specific_config)));
+}
+
+Matcher<StarboardVideoSampleInfo> MatchesVideoSampleInfo(
+    const StarboardVideoSampleInfo& expected) {
+  return AllOf(
+      Field("codec", &StarboardVideoSampleInfo::codec, Eq(expected.codec)),
+      expected.mime == nullptr
+          ? Field("mime", &StarboardVideoSampleInfo::mime, IsNull())
+          : Field("mime", &StarboardVideoSampleInfo::mime,
+                  StrEq(expected.mime)),
+      expected.max_video_capabilities == nullptr
+          ? Field("max_video_capabilities",
+                  &StarboardVideoSampleInfo::max_video_capabilities, IsNull())
+          : Field("max_video_capabilities",
+                  &StarboardVideoSampleInfo::max_video_capabilities,
+                  StrEq(expected.max_video_capabilities)),
+      Field("is_key_frame", &StarboardVideoSampleInfo::is_key_frame,
+            Eq(expected.is_key_frame)),
+      Field("frame_width", &StarboardVideoSampleInfo::frame_width,
+            Eq(expected.frame_width)),
+      Field("frame_height", &StarboardVideoSampleInfo::frame_height,
+            Eq(expected.frame_height)),
+      Field("color_metadata", &StarboardVideoSampleInfo::color_metadata,
+            MatchesColorMetadata(expected.color_metadata)));
+}
+
+Matcher<StarboardDrmSampleInfo> MatchesDrmInfo(
+    const StarboardDrmSampleInfo& expected) {
+  return AllOf(
+      Field("encryption_scheme", &StarboardDrmSampleInfo::encryption_scheme,
+            Eq(expected.encryption_scheme)),
+      Field("encryption_pattern", &StarboardDrmSampleInfo::encryption_pattern,
+            AllOf(Field("crypt_byte_block",
+                        &StarboardDrmEncryptionPattern::crypt_byte_block,
+                        Eq(expected.encryption_pattern.crypt_byte_block)),
+                  Field("skip_byte_block",
+                        &StarboardDrmEncryptionPattern::skip_byte_block,
+                        Eq(expected.encryption_pattern.skip_byte_block)))),
+      Field(
+          "initialization_vector",
+          &StarboardDrmSampleInfo::initialization_vector,
+          SpanMatches(base::span<const uint8_t>(expected.initialization_vector)
+                          .first(static_cast<size_t>(
+                              expected.initialization_vector_size)))),
+      Field("initialization_vector_size",
+            &StarboardDrmSampleInfo::initialization_vector_size,
+            Eq(expected.initialization_vector_size)),
+      Field("identifier", &StarboardDrmSampleInfo::identifier,
+            SpanMatches(
+                base::span<const uint8_t>(expected.identifier)
+                    .first(static_cast<size_t>(expected.identifier_size)))),
+      Field("identifier_size", &StarboardDrmSampleInfo::identifier_size,
+            Eq(expected.identifier_size)),
+      Field("subsample_mapping", &StarboardDrmSampleInfo::subsample_mapping,
+            Pointwise(MatchesSubsampleMappingTuple(),
+                      expected.subsample_mapping)));
+}
+
+Matcher<StarboardPlayerCreationParam> MatchesPlayerCreationParam(
+    const StarboardPlayerCreationParam& expected) {
+  return AllOf(Field("drm_system", &StarboardPlayerCreationParam::drm_system,
+                     Eq(expected.drm_system)),
+               Field("audio_sample_info",
+                     &StarboardPlayerCreationParam::audio_sample_info,
+                     MatchesAudioSampleInfo(expected.audio_sample_info)),
+               Field("video_sample_info",
+                     &StarboardPlayerCreationParam::video_sample_info,
+                     MatchesVideoSampleInfo(expected.video_sample_info)),
+               Field("output_mode", &StarboardPlayerCreationParam::output_mode,
+                     Eq(expected.output_mode)));
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/media/test_matchers.h b/chromecast/starboard/media/media/test_matchers.h
new file mode 100644
index 0000000..c2554f451
--- /dev/null
+++ b/chromecast/starboard/media/media/test_matchers.h
@@ -0,0 +1,49 @@
+// 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.
+
+// Contains matchers for starboard-related structs that can be used in tests.
+//
+// If more fine-grained matchers are needed in multiple tests, they can be moved
+// from the .cc file to this header.
+
+#ifndef CHROMECAST_STARBOARD_MEDIA_MEDIA_TEST_MATCHERS_H_
+#define CHROMECAST_STARBOARD_MEDIA_MEDIA_TEST_MATCHERS_H_
+
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace chromecast {
+namespace media {
+
+// Returns a matcher that compares against a given StarboardSampleInfo. Note
+// that the "buffer" field (a void*) is checked for equality by address. The
+// individual elements are not compared. This was done because the lifetime of
+// the buffer is important, so the actual address is usually relevant.
+//
+// Also note that when resampling occurs for PCM data, the address in "buffer"
+// may not match the one provided by a mock (since a new buffer is created
+// internally).
+::testing::Matcher<StarboardSampleInfo> MatchesStarboardSampleInfo(
+    const StarboardSampleInfo& expected);
+
+// Returns a matcher that compares against a given StarboardAudioSampleInfo.
+::testing::Matcher<StarboardAudioSampleInfo> MatchesAudioSampleInfo(
+    const StarboardAudioSampleInfo& expected);
+
+// Returns a matcher that compares against a given StarboardVideoSampleInfo.
+::testing::Matcher<StarboardVideoSampleInfo> MatchesVideoSampleInfo(
+    const StarboardVideoSampleInfo& expected);
+
+// Returns a matcher that compares against a given StarboardDrmSampleInfo.
+::testing::Matcher<StarboardDrmSampleInfo> MatchesDrmInfo(
+    const StarboardDrmSampleInfo& expected);
+
+// Returns a matcher that compares against a given StarboardPlayerCreationParam.
+::testing::Matcher<StarboardPlayerCreationParam> MatchesPlayerCreationParam(
+    const StarboardPlayerCreationParam& expected);
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_STARBOARD_MEDIA_MEDIA_TEST_MATCHERS_H_
diff --git a/chromecast/starboard/media/renderer/BUILD.gn b/chromecast/starboard/media/renderer/BUILD.gn
new file mode 100644
index 0000000..83c980a
--- /dev/null
+++ b/chromecast/starboard/media/renderer/BUILD.gn
@@ -0,0 +1,157 @@
+# Copyright 2025 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+source_set("geometry_change_handler") {
+  public = [ "geometry_change_handler.h" ]
+  sources = [ "geometry_change_handler.cc" ]
+  public_deps = [
+    "//base",
+    "//chromecast/media/service/mojom",
+    "//chromecast/starboard/media/media:starboard_api_wrapper",
+    "//ui/gfx:memory_buffer",
+    "//ui/gfx/geometry",
+  ]
+  deps = [
+    "//chromecast/media/service:video_geometry_setter_service",
+    "//mojo/public/cpp/bindings",
+    "//ui/display",
+  ]
+}
+
+source_set("starboard_player_manager") {
+  public = [ "starboard_player_manager.h" ]
+  sources = [ "starboard_player_manager.cc" ]
+  public_deps = [
+    ":client_stats_tracker",
+    ":demuxer_stream_reader",
+    "//base",
+    "//chromecast/starboard/media/cdm:starboard_drm_wrapper",
+    "//chromecast/starboard/media/media:starboard_api_wrapper",
+    "//media",
+  ]
+  deps = [ ":chromium_starboard_conversions" ]
+}
+
+source_set("demuxer_stream_reader") {
+  public = [ "demuxer_stream_reader.h" ]
+  sources = [ "demuxer_stream_reader.cc" ]
+  public_deps = [
+    "//base",
+    "//chromecast/starboard/media/media:drm_util",
+    "//chromecast/starboard/media/media:starboard_api_wrapper",
+    "//media",
+  ]
+  deps = [
+    ":chromium_starboard_conversions",
+    "//chromecast/starboard/chromecast/starboard_cast_api:cast_starboard_api_types",
+    "//chromecast/starboard/media/cdm:starboard_drm_key_tracker",
+    "//chromecast/starboard/media/media:starboard_resampler",
+  ]
+}
+
+source_set("chromium_starboard_conversions") {
+  public = [ "chromium_starboard_conversions.h" ]
+  sources = [ "chromium_starboard_conversions.cc" ]
+  public_deps = [
+    "//chromecast/starboard/media/media:starboard_api_wrapper",
+    "//media",
+  ]
+  deps = [
+    "//base",
+    "//chromecast/starboard/media/media:mime_utils",
+    "//third_party/abseil-cpp:absl",
+    "//ui/gfx:color_space",
+    "//ui/gfx/geometry",
+  ]
+}
+
+source_set("client_stats_tracker") {
+  sources = [
+    "client_stats_tracker.cc",
+    "client_stats_tracker.h",
+  ]
+  deps = [
+    "//base",
+    "//chromecast/starboard/media/media:starboard_api_wrapper",
+    "//media",
+  ]
+}
+
+test("client_stats_tracker_test") {
+  sources = [ "client_stats_tracker_test.cc" ]
+  deps = [
+    ":client_stats_tracker",
+    "//base",
+    "//base/test:run_all_unittests",
+    "//chromecast/starboard/media/media:starboard_api_wrapper",
+    "//media:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
+test("demuxer_stream_reader_test") {
+  sources = [ "demuxer_stream_reader_test.cc" ]
+  deps = [
+    ":demuxer_stream_reader",
+    "//base",
+    "//base/test:run_all_unittests",
+    "//base/test:test_support",
+    "//chromecast/starboard/media/cdm:starboard_drm_key_tracker",
+    "//chromecast/starboard/media/media:starboard_api_wrapper",
+    "//chromecast/starboard/media/media:test_matchers",
+    "//media",
+    "//media:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
+test("chromium_starboard_conversions_test") {
+  sources = [ "chromium_starboard_conversions_test.cc" ]
+  deps = [
+    ":chromium_starboard_conversions",
+    "//base/test:run_all_unittests",
+    "//chromecast/starboard/media/media:test_matchers",
+    "//media",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//ui/gfx/geometry",
+  ]
+}
+
+test("geometry_change_handler_test") {
+  sources = [ "geometry_change_handler_test.cc" ]
+  deps = [
+    ":geometry_change_handler",
+    "//base",
+    "//base/test:run_all_unittests",
+    "//base/test:test_support",
+    "//chromecast/media/service:video_geometry_setter_service",
+    "//chromecast/starboard/media/media:mock_starboard_api_wrapper",
+    "//mojo/core/embedder",
+    "//ui/display:test_support",
+    "//ui/gfx:memory_buffer",
+    "//ui/gfx/geometry",
+  ]
+}
+
+test("starboard_player_manager_test") {
+  sources = [ "starboard_player_manager_test.cc" ]
+  deps = [
+    ":starboard_player_manager",
+    "//base",
+    "//base/test:run_all_unittests",
+    "//base/test:test_support",
+    "//chromecast/starboard/media/cdm:starboard_drm_wrapper",
+    "//chromecast/starboard/media/media:mock_starboard_api_wrapper",
+    "//chromecast/starboard/media/media:starboard_api_wrapper",
+    "//chromecast/starboard/media/media:test_matchers",
+    "//media",
+    "//media:test_support",
+    "//ui/gfx/geometry",
+  ]
+}
diff --git a/chromecast/starboard/media/renderer/chromium_starboard_conversions.cc b/chromecast/starboard/media/renderer/chromium_starboard_conversions.cc
new file mode 100644
index 0000000..a845ce9
--- /dev/null
+++ b/chromecast/starboard/media/renderer/chromium_starboard_conversions.cc
@@ -0,0 +1,343 @@
+// 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 "chromecast/starboard/media/renderer/chromium_starboard_conversions.h"
+
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+#include "chromecast/starboard/media/media/mime_utils.h"
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "media/base/audio_codecs.h"
+#include "media/base/channel_layout.h"
+#include "media/base/video_codecs.h"
+#include "media/base/video_color_space.h"
+#include "third_party/abseil-cpp/absl/container/node_hash_set.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/hdr_metadata.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+using ::media::VideoColorSpace;
+
+// Registers the given MIME type, if it has not already been registered. Returns
+// a c string of the MIME type, which is guaranteed to point to valid data for
+// the duration of the program.
+const char* RegisterMimeType(std::string mime) {
+  if (mime.empty()) {
+    return "";
+  }
+
+  static base::Lock lock;
+  // A node_hash_set is used here because we require key ptr stability (so that
+  // c strings remain valid).
+  static base::NoDestructor<absl::node_hash_set<std::string>> registry
+      GUARDED_BY(lock);
+
+  base::AutoLock autlock(lock);
+  auto it_and_inserted = registry->insert(std::move(mime));
+  return it_and_inserted.first->c_str();
+}
+
+// Converts a chromium codec to a starboard codec, returning nullopt if the
+// codec does not exist in starboard.
+//
+// `profile` is necessary for differentiating Dolby Vision codecs.
+std::optional<StarboardVideoCodec> ToSbVideoCodec(
+    ::media::VideoCodec codec,
+    ::media::VideoCodecProfile profile) {
+  switch (codec) {
+    case ::media::VideoCodec::kH264:
+      return StarboardVideoCodec::kStarboardVideoCodecH264;
+    case ::media::VideoCodec::kMPEG2:
+      return StarboardVideoCodec::kStarboardVideoCodecMpeg2;
+    case ::media::VideoCodec::kTheora:
+      return StarboardVideoCodec::kStarboardVideoCodecTheora;
+    case ::media::VideoCodec::kVP8:
+      return StarboardVideoCodec::kStarboardVideoCodecVp8;
+    case ::media::VideoCodec::kVP9:
+      return StarboardVideoCodec::kStarboardVideoCodecVp9;
+    case ::media::VideoCodec::kHEVC:
+      return StarboardVideoCodec::kStarboardVideoCodecH265;
+    case ::media::VideoCodec::kAV1:
+      return StarboardVideoCodec::kStarboardVideoCodecAv1;
+    case ::media::VideoCodec::kVC1:
+      return StarboardVideoCodec::kStarboardVideoCodecVc1;
+    case ::media::VideoCodec::kDolbyVision:
+      switch (profile) {
+        // This logic was copied from
+        // https://source.chromium.org/chromium/chromium/src/+/main:chromecast/media/base/media_codec_support.cc;l=63;drc=586d9e059d27bfbe85c8df737882821e7b68929d
+        case ::media::VideoCodecProfile::DOLBYVISION_PROFILE5:
+        case ::media::VideoCodecProfile::DOLBYVISION_PROFILE7:
+        case ::media::VideoCodecProfile::DOLBYVISION_PROFILE8:
+          return StarboardVideoCodec::kStarboardVideoCodecH265;
+        default:
+          // We only support Dolby Vision for HEVC currently. The H264 profiles
+          // (DOLBYVISION_PROFILE0 and DOLBYVISION_PROFILE9) are considered
+          // unsupported.
+          LOG(INFO) << "Unsupported Dolby Vision profile=" << profile;
+          return std::nullopt;
+      }
+    default:
+      LOG(ERROR) << "Unsupported video codec: " << codec;
+      return std::nullopt;
+  }
+}
+
+// Populates HDR fields of `out_color_metadata` based on `hdr_metadata`.
+void PopulateHdrMetadata(const gfx::HDRMetadata& hdr_metadata,
+                         StarboardColorMetadata& out_color_metadata) {
+  if (hdr_metadata.cta_861_3) {
+    out_color_metadata.max_cll =
+        hdr_metadata.cta_861_3->max_content_light_level;
+    out_color_metadata.max_fall =
+        hdr_metadata.cta_861_3->max_frame_average_light_level;
+  } else {
+    LOG(INFO) << "HDR metadata is missing cta_861_3 info.";
+  }
+
+  if (hdr_metadata.smpte_st_2086) {
+    const auto& color_volume_metadata = hdr_metadata.smpte_st_2086->primaries;
+
+    StarboardMediaMasteringMetadata& mastering_metadata =
+        out_color_metadata.mastering_metadata;
+    mastering_metadata.primary_r_chromaticity_x = color_volume_metadata.fRX;
+    mastering_metadata.primary_r_chromaticity_y = color_volume_metadata.fRY;
+    mastering_metadata.primary_g_chromaticity_x = color_volume_metadata.fGX;
+    mastering_metadata.primary_g_chromaticity_y = color_volume_metadata.fGY;
+    mastering_metadata.primary_b_chromaticity_x = color_volume_metadata.fBX;
+    mastering_metadata.primary_b_chromaticity_y = color_volume_metadata.fBY;
+    mastering_metadata.white_point_chromaticity_x = color_volume_metadata.fWX;
+    mastering_metadata.white_point_chromaticity_y = color_volume_metadata.fWY;
+    mastering_metadata.luminance_max =
+        hdr_metadata.smpte_st_2086->luminance_max;
+    mastering_metadata.luminance_min =
+        hdr_metadata.smpte_st_2086->luminance_min;
+  } else {
+    LOG(INFO) << "HDR metadata is missing smpte_st_2086 info.";
+  }
+}
+
+// Converts chromium color metadata to starboard color metadata, returning
+// nullopt if the chromium color metadata cannot be converted.
+std::optional<StarboardColorMetadata> ToSbColorMetadata(
+    const std::optional<gfx::HDRMetadata>& hdr_metadata,
+    const ::media::VideoColorSpace& color_space) {
+  StarboardColorMetadata color_metadata = {};
+  // bits_per_channel and the chroma_*/cb_* fields below need to be derived from
+  // the MIME string. See crbug.com/230915942 for more info.
+  // Unfortunately, it doesn't look like MIME type is exposed to cast. Note that
+  // in Cobalt, these fields are all currently hard-coded to zero (in
+  // third_party/chromium/media/base/starboard_utils.cc). I don't think they're
+  // necessary for cast either, since cast doesn't seem to populate this info
+  // anywhere.
+
+  // 0 translates to "unknown".
+  color_metadata.bits_per_channel = 0;
+  color_metadata.chroma_subsampling_horizontal = 0;
+  color_metadata.chroma_subsampling_vertical = 0;
+  color_metadata.cb_subsampling_horizontal = 0;
+  color_metadata.cb_subsampling_vertical = 0;
+  color_metadata.chroma_siting_horizontal = 0;
+  color_metadata.chroma_siting_vertical = 0;
+
+  if (hdr_metadata) {
+    LOG(INFO) << "Video config has HDR metadata.";
+    PopulateHdrMetadata(*hdr_metadata, color_metadata);
+  } else {
+    LOG(INFO) << "Video config has no HDR metadata.";
+    color_metadata.max_cll = 0;
+    color_metadata.max_fall = 0;
+  }
+
+  switch (color_space.primaries) {
+    case VideoColorSpace::PrimaryID::INVALID:
+    case VideoColorSpace::PrimaryID::BT709:
+    case VideoColorSpace::PrimaryID::UNSPECIFIED:
+    case VideoColorSpace::PrimaryID::BT470M:
+    case VideoColorSpace::PrimaryID::BT470BG:
+    case VideoColorSpace::PrimaryID::SMPTE170M:
+    case VideoColorSpace::PrimaryID::SMPTE240M:
+    case VideoColorSpace::PrimaryID::FILM:
+    case VideoColorSpace::PrimaryID::BT2020:
+    case VideoColorSpace::PrimaryID::SMPTEST428_1:
+    case VideoColorSpace::PrimaryID::SMPTEST431_2:
+    case VideoColorSpace::PrimaryID::SMPTEST432_1:
+      color_metadata.primaries = static_cast<int>(color_space.primaries);
+      break;
+    default:
+      LOG(ERROR) << "Unsupported color space primaries: "
+                 << static_cast<int>(color_space.primaries);
+      return std::nullopt;
+  }
+
+  switch (color_space.transfer) {
+    case VideoColorSpace::TransferID::INVALID:
+    case VideoColorSpace::TransferID::BT709:
+    case VideoColorSpace::TransferID::UNSPECIFIED:
+    case VideoColorSpace::TransferID::GAMMA22:
+    case VideoColorSpace::TransferID::GAMMA28:
+    case VideoColorSpace::TransferID::SMPTE170M:
+    case VideoColorSpace::TransferID::SMPTE240M:
+    case VideoColorSpace::TransferID::LINEAR:
+    case VideoColorSpace::TransferID::LOG:
+    case VideoColorSpace::TransferID::LOG_SQRT:
+    case VideoColorSpace::TransferID::IEC61966_2_4:
+    case VideoColorSpace::TransferID::BT1361_ECG:
+    case VideoColorSpace::TransferID::IEC61966_2_1:
+    case VideoColorSpace::TransferID::BT2020_10:
+    case VideoColorSpace::TransferID::BT2020_12:
+    case VideoColorSpace::TransferID::SMPTEST2084:
+    case VideoColorSpace::TransferID::SMPTEST428_1:
+      color_metadata.transfer = static_cast<int>(color_space.transfer);
+      break;
+    default:
+      LOG(ERROR) << "Unsupported color space transfer: "
+                 << static_cast<int>(color_space.transfer);
+      return std::nullopt;
+  }
+
+  switch (color_space.matrix) {
+    case VideoColorSpace::MatrixID::RGB:
+    case VideoColorSpace::MatrixID::BT709:
+    case VideoColorSpace::MatrixID::UNSPECIFIED:
+    case VideoColorSpace::MatrixID::FCC:
+    case VideoColorSpace::MatrixID::BT470BG:
+    case VideoColorSpace::MatrixID::SMPTE170M:
+    case VideoColorSpace::MatrixID::SMPTE240M:
+    case VideoColorSpace::MatrixID::YCOCG:
+    case VideoColorSpace::MatrixID::BT2020_NCL:
+    case VideoColorSpace::MatrixID::BT2020_CL:
+    case VideoColorSpace::MatrixID::YDZDX:
+    case VideoColorSpace::MatrixID::INVALID:
+      color_metadata.matrix = static_cast<int>(color_space.matrix);
+      break;
+    default:
+      LOG(ERROR) << "Unsupported color space matrix: "
+                 << static_cast<int>(color_space.matrix);
+      return std::nullopt;
+  }
+
+  switch (color_space.range) {
+    case gfx::ColorSpace::RangeID::INVALID:
+    case gfx::ColorSpace::RangeID::LIMITED:
+    case gfx::ColorSpace::RangeID::FULL:
+    case gfx::ColorSpace::RangeID::DERIVED:
+      color_metadata.range = static_cast<int>(color_space.range);
+      break;
+    default:
+      LOG(ERROR) << "Unsupported color space range: "
+                 << static_cast<int>(color_space.range);
+      return std::nullopt;
+  }
+
+  // color_space.primaries (::media::VideoColorSpace::PrimaryID)
+  // does not support any value equivalent to Starboard's
+  // kSbMediaPrimaryIdCustom. Thus, we don't need to populate
+  // custom_primary_matrix. Just zero it, in case something reads from it.
+  return color_metadata;
+}
+
+}  // namespace
+
+std::optional<StarboardAudioSampleInfo> ToStarboardAudioSampleInfo(
+    const ::media::AudioDecoderConfig& in_config) {
+  StarboardAudioSampleInfo out_config = {};
+
+  switch (in_config.codec()) {
+    case ::media::AudioCodec::kAAC:
+      out_config.codec = StarboardAudioCodec::kStarboardAudioCodecAac;
+      break;
+    case ::media::AudioCodec::kMP3:
+      out_config.codec = StarboardAudioCodec::kStarboardAudioCodecMp3;
+      break;
+    case ::media::AudioCodec::kVorbis:
+      out_config.codec = StarboardAudioCodec::kStarboardAudioCodecVorbis;
+      break;
+    case ::media::AudioCodec::kFLAC:
+      out_config.codec = StarboardAudioCodec::kStarboardAudioCodecFlac;
+      break;
+    case ::media::AudioCodec::kPCM:
+    case ::media::AudioCodec::kPCM_S16BE:
+    case ::media::AudioCodec::kPCM_S24BE:
+      out_config.codec = StarboardAudioCodec::kStarboardAudioCodecPcm;
+      break;
+    case ::media::AudioCodec::kOpus:
+      out_config.codec = StarboardAudioCodec::kStarboardAudioCodecOpus;
+      break;
+    case ::media::AudioCodec::kEAC3:
+      out_config.codec = StarboardAudioCodec::kStarboardAudioCodecEac3;
+      break;
+    case ::media::AudioCodec::kAC3:
+      out_config.codec = StarboardAudioCodec::kStarboardAudioCodecAc3;
+      break;
+    default:
+      LOG(ERROR) << "Unsupported audio codec: " << in_config.codec();
+      return std::nullopt;
+  }
+
+  out_config.mime = RegisterMimeType(GetMimeType(in_config.codec()));
+  out_config.format_tag = 0;
+  out_config.number_of_channels = in_config.channels();
+  out_config.samples_per_second = in_config.samples_per_second();
+
+  // Based on starboard_utils.cc (MediaAudioConfigToSbMediaAudioSampleInfo) in
+  // the cobalt codebase, bits_per_sample  does not take into account the number
+  // of channels.
+  if (out_config.codec == StarboardAudioCodec::kStarboardAudioCodecPcm) {
+    // TODO(antoniori): handle resampling to other formats. Currently we only
+    // support S16.
+    out_config.bits_per_sample = 16;
+  } else {
+    // For starboard, "bits per sample" does not factor in channel count.
+    out_config.bits_per_sample = in_config.bytes_per_channel() * 8;
+  }
+  out_config.average_bytes_per_second = out_config.number_of_channels *
+                                        out_config.samples_per_second *
+                                        out_config.bits_per_sample / 8;
+  out_config.block_alignment = 4;
+  out_config.audio_specific_config_size = in_config.extra_data().size();
+  out_config.audio_specific_config = in_config.extra_data().data();
+
+  return out_config;
+}
+
+std::optional<StarboardVideoSampleInfo> ToStarboardVideoSampleInfo(
+    const ::media::VideoDecoderConfig& in_config) {
+  StarboardVideoSampleInfo out_config = {};
+
+  std::optional<StarboardVideoCodec> sb_codec =
+      ToSbVideoCodec(in_config.codec(), in_config.profile());
+  if (!sb_codec.has_value()) {
+    return std::nullopt;
+  }
+
+  out_config.codec = *sb_codec;
+  out_config.mime = RegisterMimeType(
+      GetMimeType(in_config.codec(), in_config.profile(), in_config.level()));
+  // Specify that the max capabilities are not known.
+  out_config.max_video_capabilities = "";
+  // This needs to be set on a per-sample basis later.
+  out_config.is_key_frame = false;
+
+  const gfx::Size aspect_ratio = in_config.coded_size();
+  out_config.frame_width = aspect_ratio.width();
+  out_config.frame_height = aspect_ratio.height();
+
+  std::optional<StarboardColorMetadata> sb_color_metadata =
+      ToSbColorMetadata(in_config.hdr_metadata(), in_config.color_space_info());
+  if (!sb_color_metadata.has_value()) {
+    return std::nullopt;
+  }
+  out_config.color_metadata = *std::move(sb_color_metadata);
+
+  return out_config;
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/renderer/chromium_starboard_conversions.h b/chromecast/starboard/media/renderer/chromium_starboard_conversions.h
new file mode 100644
index 0000000..d42e966
--- /dev/null
+++ b/chromecast/starboard/media/renderer/chromium_starboard_conversions.h
@@ -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.
+
+// Contains functions for converting chromium structs to equivalent starboard
+// structs.
+
+#ifndef CHROMECAST_STARBOARD_MEDIA_RENDERER_CHROMIUM_STARBOARD_CONVERSIONS_H_
+#define CHROMECAST_STARBOARD_MEDIA_RENDERER_CHROMIUM_STARBOARD_CONVERSIONS_H_
+
+#include <optional>
+
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/video_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+
+// Converts an AudioDecoderConfig to StarboardAudioSampleInfo, returning nullopt
+// if the config is not supported or is invalid.
+std::optional<StarboardAudioSampleInfo> ToStarboardAudioSampleInfo(
+    const ::media::AudioDecoderConfig& in_config);
+
+// Converts a VideoDecoderConfig to StarboardVideoSampleInfo, returning nullopt
+// if the config is not supported or is invalid.
+std::optional<StarboardVideoSampleInfo> ToStarboardVideoSampleInfo(
+    const ::media::VideoDecoderConfig& in_config);
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_STARBOARD_MEDIA_RENDERER_CHROMIUM_STARBOARD_CONVERSIONS_H_
diff --git a/chromecast/starboard/media/renderer/chromium_starboard_conversions_test.cc b/chromecast/starboard/media/renderer/chromium_starboard_conversions_test.cc
new file mode 100644
index 0000000..253e023
--- /dev/null
+++ b/chromecast/starboard/media/renderer/chromium_starboard_conversions_test.cc
@@ -0,0 +1,191 @@
+// 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 "chromecast/starboard/media/renderer/chromium_starboard_conversions.h"
+
+#include <cstring>
+#include <optional>
+#include <vector>
+
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "chromecast/starboard/media/media/test_matchers.h"
+#include "media/base/audio_codecs.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/channel_layout.h"
+#include "media/base/encryption_scheme.h"
+#include "media/base/sample_format.h"
+#include "media/base/video_codecs.h"
+#include "media/base/video_color_space.h"
+#include "media/base/video_decoder_config.h"
+#include "media/base/video_transformation.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace chromecast {
+namespace media {
+namespace {
+
+using ::media::AudioCodec;
+using ::media::AudioDecoderConfig;
+using ::media::ChannelLayout;
+using ::media::EncryptionScheme;
+using ::media::SampleFormat;
+using ::media::VideoCodec;
+using ::media::VideoCodecProfile;
+using ::media::VideoColorSpace;
+using ::media::VideoDecoderConfig;
+using ::media::VideoTransformation;
+using ::testing::Optional;
+
+TEST(StarboardConversionsTest, ConvertsValidAudioConfigToStarboardConfig) {
+  EXPECT_THAT(
+      ToStarboardAudioSampleInfo(AudioDecoderConfig(
+          AudioCodec::kAAC, SampleFormat::kSampleFormatS32,
+          ChannelLayout::CHANNEL_LAYOUT_5_1, /*samples_per_second=*/48000,
+          /*extra_data=*/{}, EncryptionScheme::kCenc)),
+      Optional(MatchesAudioSampleInfo({
+          .codec = StarboardAudioCodec::kStarboardAudioCodecAac,
+          .mime = R"-(audio/mp4; codecs="mp4a.40.5")-",
+          .format_tag = 0,
+          .number_of_channels = 6,
+          .samples_per_second = 48000,
+          .average_bytes_per_second = 48000 * 4 * 6,
+          .block_alignment = 4,
+          .bits_per_sample = 32,
+          .audio_specific_config_size = 0,
+          .audio_specific_config = nullptr,
+      })));
+}
+
+TEST(StarboardConversionsTest, ReturnsNulloptForInvalidAudioConfig) {
+  // DTS is not supported in starboard.
+  EXPECT_EQ(ToStarboardAudioSampleInfo(AudioDecoderConfig(
+                AudioCodec::kDTS, SampleFormat::kSampleFormatS32,
+                ChannelLayout::CHANNEL_LAYOUT_5_1, /*samples_per_second=*/48000,
+                /*extra_data=*/{}, EncryptionScheme::kCenc)),
+            std::nullopt);
+}
+
+TEST(StarboardConversionsTest, ConvertsValidVideoConfigToStarboardConfig) {
+  EXPECT_THAT(
+      ToStarboardVideoSampleInfo(VideoDecoderConfig(
+          VideoCodec::kHEVC, VideoCodecProfile::HEVCPROFILE_MAIN10,
+          VideoDecoderConfig::AlphaMode::kIsOpaque,
+          VideoColorSpace(1, 1, 1, gfx::ColorSpace::RangeID::LIMITED),
+          VideoTransformation(), gfx::Size(1920, 1080), gfx::Rect(1920, 1080),
+          gfx::Size(1920, 1080), /*extra_data=*/{}, EncryptionScheme::kCenc)),
+      Optional(MatchesVideoSampleInfo({
+          .codec = StarboardVideoCodec::kStarboardVideoCodecH265,
+          .mime = R"-(video/mp4; codecs="hev1.2.6.L0.B0")-",
+          .max_video_capabilities = "",
+          .is_key_frame = false,
+          .frame_width = 1920,
+          .frame_height = 1080,
+          .color_metadata =
+              StarboardColorMetadata{
+                  .bits_per_channel = 0,               // unknown
+                  .chroma_subsampling_horizontal = 0,  // unknown
+                  .chroma_subsampling_vertical = 0,    // unknown
+                  .cb_subsampling_horizontal = 0,      // unknown
+                  .cb_subsampling_vertical = 0,        // unknown
+                  .chroma_siting_horizontal = 0,       // unknown
+                  .chroma_siting_vertical = 0,         // unknown
+                  .mastering_metadata = {},
+                  .max_cll = 0,
+                  .max_fall = 0,
+                  .primaries = 1,
+                  .transfer = 1,
+                  .matrix = 1,
+                  .range = 1,
+              },
+      })));
+}
+
+TEST(StarboardConversionsTest,
+     ConvertsValidVideoConfigWithHdrMetadataToStarboardConfig) {
+  VideoDecoderConfig chromium_config(
+      VideoCodec::kHEVC, VideoCodecProfile::HEVCPROFILE_MAIN10,
+      VideoDecoderConfig::AlphaMode::kIsOpaque,
+      VideoColorSpace(9, 16, 9, gfx::ColorSpace::RangeID::LIMITED),
+      VideoTransformation(), gfx::Size(3840, 2160), gfx::Rect(3840, 2160),
+      gfx::Size(3840, 2160), /*extra_data=*/{}, EncryptionScheme::kUnencrypted);
+  gfx::HDRMetadata hdr_metadata;
+  gfx::HdrMetadataSmpteSt2086 smpte;
+  smpte.luminance_max = 0.9;
+  smpte.primaries.fRX = 0.1;
+  smpte.primaries.fRY = 0.2;
+  smpte.primaries.fGX = 0.3;
+  smpte.primaries.fGY = 0.4;
+  smpte.primaries.fBX = 0.5;
+  smpte.primaries.fBY = 0.6;
+  smpte.primaries.fWX = 0.7;
+  smpte.primaries.fWY = 0.8;
+  smpte.luminance_max = 1.1;
+  smpte.luminance_min = 0.01;
+
+  gfx::HdrMetadataCta861_3 cta;
+  cta.max_content_light_level = 100;
+  cta.max_frame_average_light_level = 1000;
+
+  hdr_metadata.smpte_st_2086 = smpte;
+  hdr_metadata.cta_861_3 = cta;
+  chromium_config.set_hdr_metadata(hdr_metadata);
+
+  EXPECT_THAT(ToStarboardVideoSampleInfo(chromium_config),
+              Optional(MatchesVideoSampleInfo({
+                  .codec = StarboardVideoCodec::kStarboardVideoCodecH265,
+                  .mime = R"-(video/mp4; codecs="hev1.2.6.L0.B0")-",
+                  .max_video_capabilities = "",
+                  .is_key_frame = false,
+                  .frame_width = 3840,
+                  .frame_height = 2160,
+                  .color_metadata =
+                      {
+                          .bits_per_channel = 0,               // unknown
+                          .chroma_subsampling_horizontal = 0,  // unknown
+                          .chroma_subsampling_vertical = 0,    // unknown
+                          .cb_subsampling_horizontal = 0,      // unknown
+                          .cb_subsampling_vertical = 0,        // unknown
+                          .chroma_siting_horizontal = 0,       // unknown
+                          .chroma_siting_vertical = 0,         // unknown
+                          .mastering_metadata =
+                              {
+                                  .primary_r_chromaticity_x = 0.1,
+                                  .primary_r_chromaticity_y = 0.2,
+                                  .primary_g_chromaticity_x = 0.3,
+                                  .primary_g_chromaticity_y = 0.4,
+                                  .primary_b_chromaticity_x = 0.5,
+                                  .primary_b_chromaticity_y = 0.6,
+                                  .white_point_chromaticity_x = 0.7,
+                                  .white_point_chromaticity_y = 0.8,
+                                  .luminance_max = 1.1,
+                                  .luminance_min = 0.01,
+                              },
+                          .max_cll = 100,
+                          .max_fall = 1000,
+                          .primaries = 9,
+                          .transfer = 16,
+                          .matrix = 9,
+                          .range = 1,
+                      },
+              })));
+}
+
+TEST(StarboardConversionsTest, ReturnsNulloptForInvalidVideoConfig) {
+  // Dolby Vision is paired with a bad profile.
+  EXPECT_EQ(
+      ToStarboardVideoSampleInfo(VideoDecoderConfig(
+          VideoCodec::kDolbyVision, VideoCodecProfile::VVCPROFILE_MAIN10,
+          VideoDecoderConfig::AlphaMode::kIsOpaque,
+          VideoColorSpace(1, 1, 1, gfx::ColorSpace::RangeID::LIMITED),
+          VideoTransformation(), gfx::Size(1920, 1080), gfx::Rect(1920, 1080),
+          gfx::Size(1920, 1080), /*extra_data=*/{}, EncryptionScheme::kCenc)),
+      std::nullopt);
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/renderer/client_stats_tracker.cc b/chromecast/starboard/media/renderer/client_stats_tracker.cc
new file mode 100644
index 0000000..ac28542
--- /dev/null
+++ b/chromecast/starboard/media/renderer/client_stats_tracker.cc
@@ -0,0 +1,68 @@
+// 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 "chromecast/starboard/media/renderer/client_stats_tracker.h"
+
+#include "base/check.h"
+#include "base/logging.h"
+
+namespace chromecast {
+namespace media {
+
+ClientStatsTracker::ClientStatsTracker(::media::RendererClient* client)
+    : client_(client) {
+  CHECK(client_);
+}
+
+ClientStatsTracker::~ClientStatsTracker() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void ClientStatsTracker::UpdateStats(const StarboardPlayerInfo& player_info,
+                                     const StarboardSampleInfo& sample_info) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (sample_info.type == StarboardMediaType::kStarboardMediaTypeAudio) {
+    UpdateAudioStats(sample_info);
+  } else if (sample_info.type == StarboardMediaType::kStarboardMediaTypeVideo) {
+    UpdateVideoStats(player_info, sample_info);
+  } else {
+    LOG(ERROR) << "Unsupported starboard media type: " << sample_info.type;
+  }
+}
+
+void ClientStatsTracker::UpdateAudioStats(
+    const StarboardSampleInfo& sample_info) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Per the documentation of RendererClient, *_decoded is a delta when passed
+  // to OnStatisticsUpdate.
+  ::media::PipelineStatistics stats;
+  stats.audio_bytes_decoded = sample_info.buffer_size;
+
+  client_->OnStatisticsUpdate(stats);
+}
+
+void ClientStatsTracker::UpdateVideoStats(
+    const StarboardPlayerInfo& player_info,
+    const StarboardSampleInfo& sample_info) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Per the documentation of RendererClient, *_decoded and *_dropped are
+  // deltas when passed to OnStatisticsUpdate.
+  ::media::PipelineStatistics stats;
+  stats.video_bytes_decoded = sample_info.buffer_size;
+  stats.video_frames_decoded =
+      player_info.total_video_frames - total_video_frames_decoded_;
+  stats.video_frames_dropped =
+      player_info.dropped_video_frames - total_video_frames_dropped_;
+
+  total_video_frames_decoded_ = player_info.total_video_frames;
+  total_video_frames_dropped_ = player_info.dropped_video_frames;
+
+  client_->OnStatisticsUpdate(stats);
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/renderer/client_stats_tracker.h b/chromecast/starboard/media/renderer/client_stats_tracker.h
new file mode 100644
index 0000000..c0c3396
--- /dev/null
+++ b/chromecast/starboard/media/renderer/client_stats_tracker.h
@@ -0,0 +1,48 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_STARBOARD_MEDIA_RENDERER_CLIENT_STATS_TRACKER_H_
+#define CHROMECAST_STARBOARD_MEDIA_RENDERER_CLIENT_STATS_TRACKER_H_
+
+#include <cstdint>
+
+#include "base/sequence_checker.h"
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "media/base/renderer_client.h"
+
+namespace chromecast {
+namespace media {
+
+// Tracks media stats and reports them to a RendererClient.
+//
+// This class is not threadsafe, and must only be used on a single sequence.
+class ClientStatsTracker {
+ public:
+  // `client` must not be null.
+  explicit ClientStatsTracker(::media::RendererClient* client);
+  ~ClientStatsTracker();
+
+  // Updates stats based on a buffer pushed to starboard.
+  void UpdateStats(const StarboardPlayerInfo& player_info,
+                   const StarboardSampleInfo& sample_info);
+
+ private:
+  // Updates stats for an audio buffer.
+  void UpdateAudioStats(const StarboardSampleInfo& sample_info);
+
+  // Updates stats for a video buffer.
+  void UpdateVideoStats(const StarboardPlayerInfo& player_info,
+                        const StarboardSampleInfo& sample_info);
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  ::media::RendererClient* client_ = nullptr;
+  uint32_t total_video_frames_decoded_ = 0;
+  uint32_t total_video_frames_dropped_ = 0;
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_STARBOARD_MEDIA_RENDERER_CLIENT_STATS_TRACKER_H_
diff --git a/chromecast/starboard/media/renderer/client_stats_tracker_test.cc b/chromecast/starboard/media/renderer/client_stats_tracker_test.cc
new file mode 100644
index 0000000..0f36350
--- /dev/null
+++ b/chromecast/starboard/media/renderer/client_stats_tracker_test.cc
@@ -0,0 +1,123 @@
+// 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 "chromecast/starboard/media/renderer/client_stats_tracker.h"
+
+#include <array>
+#include <cstdint>
+
+#include "base/containers/span.h"
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "media/base/mock_filters.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+namespace {
+
+using ::media::PipelineStatistics;
+using ::testing::AllOf;
+using ::testing::Eq;
+using ::testing::Field;
+
+auto MatchesStats(const PipelineStatistics& stats) {
+  return AllOf(Field(&PipelineStatistics::audio_bytes_decoded,
+                     Eq(stats.audio_bytes_decoded)),
+               Field(&PipelineStatistics::video_bytes_decoded,
+                     Eq(stats.video_bytes_decoded)),
+               Field(&PipelineStatistics::video_frames_decoded,
+                     Eq(stats.video_frames_decoded)),
+               Field(&PipelineStatistics::video_frames_dropped,
+                     Eq(stats.video_frames_dropped)));
+}
+
+// Creates an audio sample containing the given data.
+StarboardSampleInfo CreateAudioSample(base::span<const uint8_t> data) {
+  StarboardSampleInfo sample_info;
+  sample_info.type = kStarboardMediaTypeAudio;
+  sample_info.buffer = data.data();
+  sample_info.buffer_size = data.size();
+  sample_info.timestamp = 0;
+  sample_info.side_data = base::span<const StarboardSampleSideData>();
+  sample_info.audio_sample_info = {};
+  sample_info.drm_info = nullptr;
+
+  return sample_info;
+}
+
+// Creates a video sample containing the given data.
+StarboardSampleInfo CreateVideoSample(base::span<const uint8_t> data) {
+  StarboardSampleInfo sample_info;
+  sample_info.type = kStarboardMediaTypeVideo;
+  sample_info.buffer = data.data();
+  sample_info.buffer_size = data.size();
+  sample_info.timestamp = 0;
+  sample_info.side_data = base::span<const StarboardSampleSideData>();
+  sample_info.video_sample_info = {};
+  sample_info.drm_info = nullptr;
+
+  return sample_info;
+}
+
+TEST(ClientStatsTrackerTest, UpdatesStatsForAudioBuffer) {
+  constexpr auto kAudioData = std::to_array<uint8_t>({1, 2, 3});
+  StarboardPlayerInfo player_info = {};
+  StarboardSampleInfo sample_info = CreateAudioSample(kAudioData);
+  PipelineStatistics expected_stats;
+  expected_stats.audio_bytes_decoded = kAudioData.size();
+  expected_stats.video_bytes_decoded = 0;
+  expected_stats.video_frames_decoded = 0;
+  expected_stats.video_frames_dropped = 0;
+
+  ::media::MockRendererClient client;
+  EXPECT_CALL(client, OnStatisticsUpdate(MatchesStats(expected_stats)))
+      .Times(1);
+
+  ClientStatsTracker stats_tracker(&client);
+
+  stats_tracker.UpdateStats(player_info, sample_info);
+}
+
+TEST(ClientStatsTrackerTest, UpdatesStatsForVideoBuffer) {
+  constexpr auto kVideoData1 = std::to_array<uint8_t>({1, 2, 3, 4, 5});
+  constexpr auto kVideoData2 = std::to_array<uint8_t>({6, 7, 8});
+
+  StarboardPlayerInfo player_info_1 = {};
+  player_info_1.total_video_frames = 2;
+  player_info_1.dropped_video_frames = 0;
+
+  StarboardSampleInfo sample_info_1 = CreateVideoSample(kVideoData1);
+  PipelineStatistics expected_stats_1;
+  expected_stats_1.audio_bytes_decoded = 0;
+  expected_stats_1.video_bytes_decoded = kVideoData1.size();
+  expected_stats_1.video_frames_decoded = 2;
+  expected_stats_1.video_frames_dropped = 0;
+
+  StarboardPlayerInfo player_info_2 = {};
+  player_info_2.total_video_frames = 3;
+  player_info_2.dropped_video_frames = 0;
+
+  StarboardSampleInfo sample_info_2 = CreateVideoSample(kVideoData2);
+  PipelineStatistics expected_stats_2;
+  expected_stats_2.audio_bytes_decoded = 0;
+  expected_stats_2.video_bytes_decoded = kVideoData2.size();
+  expected_stats_2.video_frames_decoded = 1;
+  expected_stats_2.video_frames_dropped = 0;
+
+  ::media::MockRendererClient client;
+  EXPECT_CALL(client, OnStatisticsUpdate(MatchesStats(expected_stats_1)))
+      .Times(1);
+  EXPECT_CALL(client, OnStatisticsUpdate(MatchesStats(expected_stats_2)))
+      .Times(1);
+
+  ClientStatsTracker stats_tracker(&client);
+
+  stats_tracker.UpdateStats(player_info_1, sample_info_1);
+  stats_tracker.UpdateStats(player_info_2, sample_info_2);
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/renderer/demuxer_stream_reader.cc b/chromecast/starboard/media/renderer/demuxer_stream_reader.cc
new file mode 100644
index 0000000..50f641dc
--- /dev/null
+++ b/chromecast/starboard/media/renderer/demuxer_stream_reader.cc
@@ -0,0 +1,327 @@
+// 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 "chromecast/starboard/media/renderer/demuxer_stream_reader.h"
+
+#include "base/check.h"
+#include "base/containers/span.h"
+#include "base/functional/bind.h"
+#include "base/hash/hash.h"
+#include "base/logging.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/sequenced_task_runner.h"
+#include "chromecast/starboard/chromecast/starboard_cast_api/cast_starboard_api_types.h"
+#include "chromecast/starboard/media/cdm/starboard_drm_key_tracker.h"
+#include "chromecast/starboard/media/media/starboard_resampler.h"
+#include "chromecast/starboard/media/renderer/chromium_starboard_conversions.h"
+#include "media/base/video_decoder_config.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+using ::media::DecoderBuffer;
+
+scoped_refptr<DecoderBuffer> ConvertPcmAudioBufferToS16(
+    ::media::AudioCodec codec,
+    ::media::SampleFormat sample_format,
+    int channel_count,
+    scoped_refptr<DecoderBuffer> buffer) {
+  return DecoderBuffer::FromArray(
+      chromecast::media::ResamplePCMAudioDataForStarboard(
+          StarboardPcmSampleFormat::kStarboardPcmSampleFormatS16, sample_format,
+          codec, channel_count, *buffer));
+}
+
+// Function used to "convert" buffers that do not need to be converted.
+scoped_refptr<DecoderBuffer> DoNotConvertBuffer(
+    scoped_refptr<DecoderBuffer> buffer) {
+  return buffer;
+}
+
+// Returns whether it is necessary to resample audio specified by `audio_config`
+// to S16.
+bool IsResamplingNecessary(const ::media::AudioDecoderConfig& audio_config) {
+  return (audio_config.codec() == ::media::AudioCodec::kPCM &&
+          audio_config.sample_format() !=
+              ::media::SampleFormat::kSampleFormatS16) ||
+         audio_config.codec() == ::media::AudioCodec::kPCM_S16BE ||
+         audio_config.codec() == ::media::AudioCodec::kPCM_S24BE;
+}
+
+}  // namespace
+
+DemuxerStreamReader::DemuxerStreamReader(
+    ::media::DemuxerStream* audio_stream,
+    ::media::DemuxerStream* video_stream,
+    std::optional<StarboardAudioSampleInfo> audio_sample_info,
+    std::optional<StarboardVideoSampleInfo> video_sample_info,
+    HandleBufferCb handle_buffer_cb,
+    HandleEosCb handle_eos_cb,
+    ::media::RendererClient* client)
+    : handle_buffer_cb_(std::move(handle_buffer_cb)),
+      handle_eos_cb_(std::move(handle_eos_cb)),
+      client_(client),
+      audio_stream_(audio_stream),
+      video_stream_(video_stream),
+      audio_sample_info_(std::move(audio_sample_info)),
+      video_sample_info_(std::move(video_sample_info)) {
+  if (audio_stream_) {
+    ::media::AudioDecoderConfig audio_config =
+        audio_stream_->audio_decoder_config();
+
+    if (IsResamplingNecessary(audio_config)) {
+      convert_audio_fn_ = base::BindRepeating(
+          &ConvertPcmAudioBufferToS16, audio_config.codec(),
+          audio_config.sample_format(), audio_config.channels());
+    } else {
+      convert_audio_fn_ = base::BindRepeating(&DoNotConvertBuffer);
+    }
+  }
+}
+
+DemuxerStreamReader::~DemuxerStreamReader() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  for (const auto& [token, unused] : token_to_drm_key_cb_) {
+    StarboardDrmKeyTracker::GetInstance().UnregisterCallback(token);
+  }
+}
+
+void DemuxerStreamReader::ReadBuffer(int seek_ticket, StarboardMediaType type) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  ::media::DemuxerStream* stream =
+      type == StarboardMediaType::kStarboardMediaTypeAudio ? audio_stream_
+                                                           : video_stream_;
+  CHECK(stream);
+  stream->Read(1,
+               base::BindOnce(&DemuxerStreamReader::OnReadBuffer,
+                              weak_factory_.GetWeakPtr(), type, seek_ticket));
+}
+
+void DemuxerStreamReader::HandleNonOkDemuxerStatus(
+    ::media::DemuxerStream::Status status,
+    StarboardMediaType type,
+    int seek_ticket) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  switch (status) {
+    case ::media::DemuxerStream::Status::kAborted: {
+      LOG(ERROR) << "DemuxerStream was aborted.";
+      // This can happen if a flush occurs while we were trying to read from a
+      // DemuxerStream. In that case, upstream code will call StartPlayingFrom
+      // again, so we should not do another read here.
+      return;
+    }
+    case ::media::DemuxerStream::Status::kConfigChanged: {
+      if (type == StarboardMediaType::kStarboardMediaTypeAudio) {
+        UpdateAudioConfig();
+
+        // Keep reading more data.
+        audio_stream_->Read(
+            1, base::BindOnce(&DemuxerStreamReader::OnReadBuffer,
+                              weak_factory_.GetWeakPtr(), type, seek_ticket));
+      } else {
+        CHECK_EQ(type, StarboardMediaType::kStarboardMediaTypeVideo);
+        UpdateVideoConfig();
+
+        // Keep reading more data.
+        video_stream_->Read(
+            1, base::BindOnce(&DemuxerStreamReader::OnReadBuffer,
+                              weak_factory_.GetWeakPtr(), type, seek_ticket));
+      }
+      return;
+    }
+    case ::media::DemuxerStream::Status::kError:
+      LOG(ERROR) << "DemuxerStream error occurred";
+      client_->OnError(::media::PIPELINE_ERROR_READ);
+      return;
+    case ::media::DemuxerStream::Status::kOk:
+      LOG(FATAL) << "OK status must be handled separately";
+    default:
+      LOG(WARNING) << "Received unknown DemuxerStream status: " << status
+                   << ", with name "
+                   << ::media::DemuxerStream::GetStatusName(status);
+      return;
+  }
+}
+
+void DemuxerStreamReader::OnReadBuffer(
+    StarboardMediaType type,
+    int seek_ticket,
+    ::media::DemuxerStream::Status status,
+    std::vector<scoped_refptr<DecoderBuffer>> buffers) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (status != ::media::DemuxerStream::Status::kOk) {
+    DCHECK(buffers.empty());
+    HandleNonOkDemuxerStatus(status, type, seek_ticket);
+    return;
+  }
+
+  CHECK_EQ(buffers.size(), 1UL);
+  scoped_refptr<DecoderBuffer> buffer = std::move(buffers[0]);
+  CHECK(buffer);
+
+  if (buffer->end_of_stream()) {
+    handle_eos_cb_.Run(seek_ticket, type);
+    return;
+  }
+
+  if (type == StarboardMediaType::kStarboardMediaTypeAudio) {
+    buffer = convert_audio_fn_.Run(std::move(buffer));
+  }
+
+  StarboardSampleInfo sample_info = {};
+  sample_info.type = type;
+  sample_info.buffer = buffer->data();
+  sample_info.buffer_size = buffer->size();
+  sample_info.timestamp = buffer->timestamp().InMicroseconds();
+  sample_info.side_data = base::span<const StarboardSampleSideData>();
+
+  if (type == StarboardMediaType::kStarboardMediaTypeAudio) {
+    DCHECK(audio_sample_info_);
+    sample_info.audio_sample_info = *audio_sample_info_;
+  } else {
+    DCHECK(video_sample_info_);
+    sample_info.video_sample_info = *video_sample_info_;
+    // is_key_frame needs to be set per sample.
+    sample_info.video_sample_info.is_key_frame = buffer->is_key_frame();
+
+    if (first_video_frame_) {
+      first_video_frame_ = false;
+      client_->OnVideoNaturalSizeChange(
+          gfx::Size(sample_info.video_sample_info.frame_width,
+                    sample_info.video_sample_info.frame_height));
+    }
+  }
+
+  // drm_info must not be deleted until after sample_info is passed to
+  // starboard.
+  DrmInfoWrapper drm_info = DrmInfoWrapper::Create(*buffer);
+  sample_info.drm_info = drm_info.GetDrmSampleInfo();
+
+  // For encrypted buffers, we should not push data to starboard util the
+  // buffer's DRM key is available to the CDM. To accomplish this, we check with
+  // the StarboardDrmKeyTracker singleton -- which is updated by the CDM,
+  // StarboardDecryptorCast -- to see whether the key is available. If the key
+  // is not available yet, we register a callback that will be run once the key
+  // becomes available.
+  if (sample_info.drm_info) {
+    const std::string drm_key(
+        reinterpret_cast<const char*>(sample_info.drm_info->identifier),
+        sample_info.drm_info->identifier_size);
+    if (!StarboardDrmKeyTracker::GetInstance().HasKey(drm_key)) {
+      WaitForKey(std::move(drm_info), std::move(sample_info), std::move(buffer),
+                 seek_ticket);
+      return;
+    }
+
+    // The key is already available; continue the logic of pushing the buffer to
+    // starboard.
+  }
+
+  handle_buffer_cb_.Run(seek_ticket, std::move(sample_info), std::move(buffer));
+}
+
+void DemuxerStreamReader::WaitForKey(DrmInfoWrapper drm_info,
+                                     StarboardSampleInfo sample_info,
+                                     scoped_refptr<DecoderBuffer> buffer,
+                                     int seek_ticket) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  StarboardDrmSampleInfo* const drm_sample_info = drm_info.GetDrmSampleInfo();
+  const std::string drm_key(
+      reinterpret_cast<const char*>(drm_sample_info->identifier),
+      drm_sample_info->identifier_size);
+
+  const size_t key_hash = base::FastHash(
+      base::span(drm_sample_info->identifier)
+          .first(static_cast<size_t>(drm_sample_info->identifier_size)));
+  LOG(INFO) << "Waiting for DRM key with hash: " << key_hash;
+  CHECK(base::SequencedTaskRunner::HasCurrentDefault());
+  const int64_t token = StarboardDrmKeyTracker::GetInstance().WaitForKey(
+      drm_key,
+      base::BindPostTask(
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          base::BindOnce(&DemuxerStreamReader::RunPendingDrmKeyCallback,
+                         weak_factory_.GetWeakPtr())));
+
+  CHECK(!token_to_drm_key_cb_.contains(token))
+      << "Got duplicate DRM key token: " << token;
+
+  // Bind the buffer to a closure that will be run when the DRM key is
+  // available.
+  base::OnceClosure handle_buffer_closure =
+      base::BindOnce(handle_buffer_cb_, seek_ticket, std::move(sample_info),
+                     std::move(buffer));
+
+  // Bind the DrmInfoWrapper as well, so that it outlives handle_buffer_closure.
+  token_to_drm_key_cb_[token] = base::BindOnce(
+      [](base::OnceClosure cb, DrmInfoWrapper drm_info) {
+        // drm_info must outlive this call. Otherwise, the pointers in the
+        // sample_info could point to bad memory.
+        std::move(cb).Run();
+      },
+      std::move(handle_buffer_closure), std::move(drm_info));
+
+  client_->OnWaiting(::media::WaitingReason::kNoDecryptionKey);
+}
+
+void DemuxerStreamReader::RunPendingDrmKeyCallback(int64_t token) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (auto it = token_to_drm_key_cb_.find(token);
+      it != token_to_drm_key_cb_.end()) {
+    std::move(it->second).Run();
+    token_to_drm_key_cb_.erase(it);
+  }
+}
+
+void DemuxerStreamReader::UpdateAudioConfig() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  CHECK(audio_stream_);
+  chromium_audio_config_ = audio_stream_->audio_decoder_config();
+  LOG(INFO) << "Audio config changed to "
+            << chromium_audio_config_.AsHumanReadableString();
+  audio_sample_info_ = ToStarboardAudioSampleInfo(chromium_audio_config_);
+  if (IsResamplingNecessary(chromium_audio_config_)) {
+    convert_audio_fn_ = base::BindRepeating(
+        &ConvertPcmAudioBufferToS16, chromium_audio_config_.codec(),
+        chromium_audio_config_.sample_format(),
+        chromium_audio_config_.channels());
+  } else {
+    convert_audio_fn_ = base::BindRepeating(&DoNotConvertBuffer);
+  }
+  client_->OnAudioConfigChange(chromium_audio_config_);
+}
+
+void DemuxerStreamReader::UpdateVideoConfig() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  DCHECK(video_stream_);
+  const ::media::VideoDecoderConfig video_config =
+      video_stream_->video_decoder_config();
+  LOG(INFO) << "Video config changed to "
+            << video_config.AsHumanReadableString();
+  std::optional<StarboardVideoSampleInfo> new_sample_info =
+      ToStarboardVideoSampleInfo(video_config);
+
+  // TODO(antoniori): maybe fail gracefully here, rather than crashing.
+  DCHECK(new_sample_info);
+  if (!video_sample_info_ ||
+      video_sample_info_->frame_width != new_sample_info->frame_width ||
+      video_sample_info_->frame_height != new_sample_info->frame_height) {
+    client_->OnVideoNaturalSizeChange(gfx::Size(
+        video_sample_info_->frame_width, video_sample_info_->frame_height));
+  }
+  video_sample_info_ = std::move(new_sample_info);
+  client_->OnVideoConfigChange(video_config);
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/renderer/demuxer_stream_reader.h b/chromecast/starboard/media/renderer/demuxer_stream_reader.h
new file mode 100644
index 0000000..5d27f30
--- /dev/null
+++ b/chromecast/starboard/media/renderer/demuxer_stream_reader.h
@@ -0,0 +1,126 @@
+// 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 CHROMECAST_STARBOARD_MEDIA_RENDERER_DEMUXER_STREAM_READER_H_
+#define CHROMECAST_STARBOARD_MEDIA_RENDERER_DEMUXER_STREAM_READER_H_
+
+#include <optional>
+
+#include "base/containers/flat_map.h"
+#include "base/functional/callback.h"
+#include "base/functional/callback_forward.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "chromecast/starboard/media/media/drm_util.h"
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/renderer_client.h"
+
+namespace chromecast {
+namespace media {
+
+// Receives buffers from one or more DemuxerStreams, calling handle_buffer_cb
+// when a buffer is ready to be processed. Audio buffers are processed via
+// `convert_audio_fn` before being passed to starboard, e.g. to convert PCM
+// data to S16.
+//
+// This class ensures that encrypted buffers are not handled until the
+// relevant DRM key is available to the CDM.
+//
+// This class must only be used on one sequence.
+class DemuxerStreamReader {
+ public:
+  // Note that this contains both a StarboardSampleInfo and a
+  // scoped_refptr<DecoderBuffer>. This is so that the lifetime of the
+  // underlying buffer data (stored in the scoped_refptr) can be managed
+  // properly.
+  //
+  // sample_info may contain pointers (to DRM-related info) that become
+  // invalid after the callback returns.
+  using HandleBufferCb = base::RepeatingCallback<void(
+      int seek_ticket,
+      StarboardSampleInfo sample_info,
+      scoped_refptr<::media::DecoderBuffer> buffer)>;
+
+  using HandleEosCb =
+      base::RepeatingCallback<void(int seek_ticket, StarboardMediaType type)>;
+
+  DemuxerStreamReader(::media::DemuxerStream* audio_stream,
+                      ::media::DemuxerStream* video_stream,
+                      std::optional<StarboardAudioSampleInfo> audio_sample_info,
+                      std::optional<StarboardVideoSampleInfo> video_sample_info,
+                      HandleBufferCb handle_buffer_cb,
+                      HandleEosCb handle_eos_cb,
+                      ::media::RendererClient* client);
+
+  ~DemuxerStreamReader();
+
+  // Requests that a buffer of type `type` be read from the relevant
+  // DemuxerStream. `seek_ticket` and `type` will be passed to the
+  // HandleBufferCb along with the buffer.
+  void ReadBuffer(int seek_ticket, StarboardMediaType type);
+
+ private:
+  using ConvertAudioFn =
+      base::RepeatingCallback<scoped_refptr<::media::DecoderBuffer>(
+          scoped_refptr<::media::DecoderBuffer>)>;
+
+  // Callback called by a DemuxerStream.
+  void OnReadBuffer(StarboardMediaType type,
+                    int seek_ticket,
+                    ::media::DemuxerStream::Status status,
+                    std::vector<scoped_refptr<::media::DecoderBuffer>> buffers);
+
+  // Handles a non-OK status from a DemuxerStream of type `type`. `status` must
+  // NOT be OK.
+  void HandleNonOkDemuxerStatus(::media::DemuxerStream::Status status,
+                                StarboardMediaType type,
+                                int seek_ticket);
+
+  // Waits for a DRM key to be available to the CDM. Once the key is available,
+  // the buffer will be pushed to starboard via handle_buffer_cb_.
+  void WaitForKey(DrmInfoWrapper drm_info,
+                  StarboardSampleInfo sample_info,
+                  scoped_refptr<::media::DecoderBuffer> buffer,
+                  int seek_ticket);
+
+  // Runs a pending callback now that a DRM key is available.
+  void RunPendingDrmKeyCallback(int64_t token);
+
+  // Updates the audio config to match the current config from audio_stream_.
+  void UpdateAudioConfig();
+
+  // Updates the video config to match the current config from video_stream_.
+  void UpdateVideoConfig();
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  ConvertAudioFn convert_audio_fn_;
+  HandleBufferCb handle_buffer_cb_;
+  HandleEosCb handle_eos_cb_;
+  ::media::RendererClient* client_;
+  ::media::DemuxerStream* audio_stream_ = nullptr;
+  ::media::DemuxerStream* video_stream_ = nullptr;
+
+  // StarboardAudioSampleInfo contains a const void* audio_specific_config. That
+  // field can point to the extra data of this config, so we should ensure that
+  // the ptr remains valid until the next buffer is read.
+  ::media::AudioDecoderConfig chromium_audio_config_;
+  std::optional<StarboardAudioSampleInfo> audio_sample_info_;
+  std::optional<StarboardVideoSampleInfo> video_sample_info_;
+  bool first_video_frame_ = true;
+  // Maps from opaque token to a callback that should be run when the DRM key
+  // represented by that token is available.
+  base::flat_map<int64_t, base::OnceClosure> token_to_drm_key_cb_;
+
+  // This should be destructed first, to invalidate any weak ptrs.
+  base::WeakPtrFactory<DemuxerStreamReader> weak_factory_{this};
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_STARBOARD_MEDIA_RENDERER_DEMUXER_STREAM_READER_H_
diff --git a/chromecast/starboard/media/renderer/demuxer_stream_reader_test.cc b/chromecast/starboard/media/renderer/demuxer_stream_reader_test.cc
new file mode 100644
index 0000000..9f8b901
--- /dev/null
+++ b/chromecast/starboard/media/renderer/demuxer_stream_reader_test.cc
@@ -0,0 +1,435 @@
+// 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 "chromecast/starboard/media/renderer/demuxer_stream_reader.h"
+
+#include <array>
+#include <cstdint>
+#include <functional>
+#include <string>
+#include <string_view>
+#include <tuple>
+
+#include "base/run_loop.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "chromecast/starboard/media/cdm/starboard_drm_key_tracker.h"
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "chromecast/starboard/media/media/test_matchers.h"
+#include "media/base/audio_codecs.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/channel_layout.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/decrypt_config.h"
+#include "media/base/encryption_scheme.h"
+#include "media/base/mock_filters.h"
+#include "media/base/sample_format.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+namespace {
+
+using ::base::test::SingleThreadTaskEnvironment;
+using ::media::AudioDecoderConfig;
+using ::media::DecoderBuffer;
+using ::media::DecryptConfig;
+using ::media::DemuxerStream;
+using ::media::MockDemuxerStream;
+using ::media::MockRendererClient;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::MockFunction;
+using ::testing::NotNull;
+using ::testing::Pointee;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::SaveArgByMove;
+
+// Some default data. The values themselves are not relevant to the logic of the
+// DemuxerStreamReader.
+constexpr auto kBufferData = std::to_array<uint8_t>({1, 2, 3, 4, 5});
+constexpr std::string_view kIv = "0123456789ABCDEF";
+static_assert(kIv.size() == DecryptConfig::kDecryptionKeySize);
+constexpr std::string_view kIdentifier = "drm_key_1";
+constexpr StarboardDrmSubSampleMapping kSubsampleMapping = {
+    .clear_byte_count = 0,
+    .encrypted_byte_count = kBufferData.size(),
+};
+
+// Matches a DecoderBuffer.
+MATCHER_P(DecoderBufferMatches, expected_buffer, "") {
+  const DecoderBuffer& decoder_buffer = arg;
+  return decoder_buffer.MatchesForTesting(expected_buffer);
+}
+
+// Creates an audio sample containing the given data and (optional) DRM info.
+StarboardSampleInfo CreateAudioSample(
+    base::span<const uint8_t> data,
+    StarboardDrmSampleInfo* drm_info = nullptr) {
+  StarboardSampleInfo sample_info;
+  sample_info.type = 0;  // Audio
+  sample_info.buffer = data.data();
+  sample_info.buffer_size = data.size();
+  sample_info.timestamp = 0;
+  sample_info.side_data = base::span<const StarboardSampleSideData>();
+  sample_info.audio_sample_info = {};
+  sample_info.audio_sample_info.codec =
+      StarboardAudioCodec::kStarboardAudioCodecAac;
+  sample_info.audio_sample_info.mime = "";
+  sample_info.drm_info = drm_info;
+
+  return sample_info;
+}
+
+// Creates a video sample containing the given data and (optional) DRM info.
+StarboardSampleInfo CreateVideoSample(
+    base::span<const uint8_t> data,
+    StarboardDrmSampleInfo* drm_info = nullptr) {
+  StarboardSampleInfo sample_info;
+  sample_info.type = 1;  // Video
+  sample_info.buffer = data.data();
+  sample_info.buffer_size = data.size();
+  sample_info.timestamp = 0;
+  sample_info.side_data = base::span<const StarboardSampleSideData>();
+  sample_info.video_sample_info.codec =
+      StarboardVideoCodec::kStarboardVideoCodecH265;
+  sample_info.video_sample_info.mime = "";
+  sample_info.video_sample_info.max_video_capabilities = "";
+  sample_info.video_sample_info.is_key_frame = false;
+  sample_info.video_sample_info.frame_width = 1920;
+  sample_info.video_sample_info.frame_height = 1080;
+  sample_info.video_sample_info.color_metadata = {};
+  sample_info.drm_info = drm_info;
+
+  return sample_info;
+}
+
+// A test fixture is used to avoid a bit of boilerplate in each test (creating
+// the task environment, mocks, etc).
+class DemuxerStreamReaderTest : public ::testing::Test {
+ protected:
+  DemuxerStreamReaderTest()
+      : audio_stream_(DemuxerStream::Type::AUDIO),
+        video_stream_(DemuxerStream::Type::VIDEO) {
+    // Ensure that tests begin with a clean slate regarding DRM keys.
+    StarboardDrmKeyTracker::GetInstance().ClearStateForTesting();
+  }
+
+  ~DemuxerStreamReaderTest() override = default;
+
+  // This should be destructed last.
+  SingleThreadTaskEnvironment task_environment_;
+  MockDemuxerStream audio_stream_;
+  MockDemuxerStream video_stream_;
+  MockRendererClient renderer_client_;
+  MockFunction<void(int seek_ticket,
+                    StarboardSampleInfo sample_info,
+                    scoped_refptr<::media::DecoderBuffer> buffer)>
+      handle_buffer_cb_;
+  MockFunction<void(int seek_ticket, StarboardMediaType type)> handle_eos_cb_;
+};
+
+TEST_F(DemuxerStreamReaderTest, ReadsVideoBufferAndCallsBufferCb) {
+  constexpr int kSeekTicket = 7;
+  scoped_refptr<DecoderBuffer> buffer =
+      DecoderBuffer::CopyFrom({1, 2, 3, 4, 5});
+  StarboardSampleInfo expected_info = CreateVideoSample(*buffer);
+
+  EXPECT_CALL(handle_eos_cb_, Call).Times(0);
+  EXPECT_CALL(handle_buffer_cb_,
+              Call(kSeekTicket, MatchesStarboardSampleInfo(expected_info),
+                   Pointee(DecoderBufferMatches(std::cref(*buffer)))))
+      .Times(1);
+  base::OnceCallback<void(
+      DemuxerStream::Status status,
+      std::vector<scoped_refptr<::media::DecoderBuffer>> buffers)>
+      read_cb;
+  EXPECT_CALL(video_stream_, OnRead).WillOnce(SaveArgByMove<0>(&read_cb));
+
+  DemuxerStreamReader stream_reader(
+      /*audio_stream=*/nullptr, &video_stream_,
+      /*audio_sample_info=*/std::nullopt, expected_info.video_sample_info,
+      base::BindLambdaForTesting(handle_buffer_cb_.AsStdFunction()),
+      base::BindLambdaForTesting(handle_eos_cb_.AsStdFunction()),
+      &renderer_client_);
+  stream_reader.ReadBuffer(kSeekTicket,
+                           StarboardMediaType::kStarboardMediaTypeVideo);
+
+  // Simulate the DemuxerStream providing a buffer.
+  ASSERT_FALSE(read_cb.is_null());
+  std::move(read_cb).Run(DemuxerStream::Status::kOk, {buffer});
+}
+
+TEST_F(DemuxerStreamReaderTest, ReadsVideoBufferAndCallsEosCb) {
+  constexpr int kSeekTicket = 7;
+  StarboardVideoSampleInfo video_sample_info = {};
+
+  EXPECT_CALL(handle_eos_cb_,
+              Call(kSeekTicket, StarboardMediaType::kStarboardMediaTypeVideo))
+      .Times(1);
+  EXPECT_CALL(handle_buffer_cb_, Call).Times(0);
+  base::OnceCallback<void(
+      DemuxerStream::Status status,
+      std::vector<scoped_refptr<::media::DecoderBuffer>> buffers)>
+      read_cb;
+  EXPECT_CALL(video_stream_, OnRead).WillOnce(SaveArgByMove<0>(&read_cb));
+
+  DemuxerStreamReader stream_reader(
+      /*audio_stream=*/nullptr, &video_stream_,
+      /*audio_sample_info=*/std::nullopt, video_sample_info,
+      base::BindLambdaForTesting(handle_buffer_cb_.AsStdFunction()),
+      base::BindLambdaForTesting(handle_eos_cb_.AsStdFunction()),
+      &renderer_client_);
+  stream_reader.ReadBuffer(kSeekTicket,
+                           StarboardMediaType::kStarboardMediaTypeVideo);
+
+  // Simulate the DemuxerStream providing a buffer.
+  ASSERT_FALSE(read_cb.is_null());
+  std::move(read_cb).Run(DemuxerStream::Status::kOk,
+                         {DecoderBuffer::CreateEOSBuffer()});
+}
+
+TEST_F(DemuxerStreamReaderTest, ReadsAudioBufferAndCallsBufferCb) {
+  constexpr int kSeekTicket = 7;
+  scoped_refptr<DecoderBuffer> buffer =
+      DecoderBuffer::CopyFrom({1, 2, 3, 4, 5, 6, 7, 8});
+  StarboardSampleInfo expected_info = CreateAudioSample(*buffer);
+
+  EXPECT_CALL(handle_eos_cb_, Call).Times(0);
+  EXPECT_CALL(handle_buffer_cb_,
+              Call(kSeekTicket, MatchesStarboardSampleInfo(expected_info),
+                   Pointee(DecoderBufferMatches(std::cref(*buffer)))))
+      .Times(1);
+  base::OnceCallback<void(
+      DemuxerStream::Status status,
+      std::vector<scoped_refptr<::media::DecoderBuffer>> buffers)>
+      read_cb;
+  EXPECT_CALL(audio_stream_, OnRead).WillOnce(SaveArgByMove<0>(&read_cb));
+
+  DemuxerStreamReader stream_reader(
+      &audio_stream_, /*video_stream=*/nullptr, expected_info.audio_sample_info,
+      /*video_sample_info=*/std::nullopt,
+      base::BindLambdaForTesting(handle_buffer_cb_.AsStdFunction()),
+      base::BindLambdaForTesting(handle_eos_cb_.AsStdFunction()),
+      &renderer_client_);
+  stream_reader.ReadBuffer(kSeekTicket,
+                           StarboardMediaType::kStarboardMediaTypeAudio);
+
+  // Simulate the DemuxerStream providing a buffer.
+  ASSERT_FALSE(read_cb.is_null());
+  std::move(read_cb).Run(DemuxerStream::Status::kOk, {buffer});
+}
+
+TEST_F(DemuxerStreamReaderTest, ReadsAudioBufferAndCallsEosCb) {
+  constexpr int kSeekTicket = 7;
+  StarboardAudioSampleInfo audio_sample_info = {};
+
+  EXPECT_CALL(handle_eos_cb_,
+              Call(kSeekTicket, StarboardMediaType::kStarboardMediaTypeAudio))
+      .Times(1);
+  EXPECT_CALL(handle_buffer_cb_, Call).Times(0);
+  base::OnceCallback<void(
+      DemuxerStream::Status status,
+      std::vector<scoped_refptr<::media::DecoderBuffer>> buffers)>
+      read_cb;
+  EXPECT_CALL(audio_stream_, OnRead).WillOnce(SaveArgByMove<0>(&read_cb));
+
+  DemuxerStreamReader stream_reader(
+      &audio_stream_, /*video_stream=*/nullptr, audio_sample_info,
+      /*video_sample_info=*/std::nullopt,
+      base::BindLambdaForTesting(handle_buffer_cb_.AsStdFunction()),
+      base::BindLambdaForTesting(handle_eos_cb_.AsStdFunction()),
+      &renderer_client_);
+  stream_reader.ReadBuffer(kSeekTicket,
+                           StarboardMediaType::kStarboardMediaTypeAudio);
+
+  // Simulate the DemuxerStream providing a buffer.
+  ASSERT_FALSE(read_cb.is_null());
+  std::move(read_cb).Run(DemuxerStream::Status::kOk,
+                         {DecoderBuffer::CreateEOSBuffer()});
+}
+
+TEST_F(DemuxerStreamReaderTest, ReadsAudioBufferAndConvertsPcmToS16) {
+  constexpr int kSeekTicket = 7;
+  // These values represent the min value, the max value, and 0. These can
+  // easily be converted to S16 for comparison (see kS16Data below).
+  constexpr auto kS32Data =
+      std::to_array<uint32_t>({0x80000000, 0x7FFFFFFF, 0});
+  // The equivalent to kS32Data, but represented as 16-bit samples.
+  constexpr auto kS16Data = std::to_array<uint16_t>({0x8000, 0x7FFF, 0});
+  scoped_refptr<DecoderBuffer> buffer =
+      DecoderBuffer::CopyFrom(base::as_byte_span(kS32Data));
+  scoped_refptr<DecoderBuffer> s16_buffer =
+      DecoderBuffer::CopyFrom(base::as_byte_span(kS16Data));
+  StarboardSampleInfo expected_info = CreateAudioSample(*buffer);
+  expected_info.audio_sample_info.codec =
+      StarboardAudioCodec::kStarboardAudioCodecPcm;
+
+  // The relevant bits of information here are:
+  // * codec = PCM (so that the DemuxerStreamReader converts it to S16)
+  // * sample format = S32
+  // * channel layout = mono (since there are only 3 samples in kS32Data)
+  // * encryption scheme = unencrypted
+  audio_stream_.set_audio_decoder_config(AudioDecoderConfig(
+      ::media::AudioCodec::kPCM, ::media::SampleFormat::kSampleFormatS32,
+      ::media::ChannelLayout::CHANNEL_LAYOUT_MONO, 44100, {},
+      ::media::EncryptionScheme::kUnencrypted));
+
+  EXPECT_CALL(handle_eos_cb_, Call).Times(0);
+  scoped_refptr<DecoderBuffer> captured_buffer;
+  StarboardSampleInfo captured_sample_info;
+  EXPECT_CALL(handle_buffer_cb_,
+              Call(kSeekTicket, _,
+                   Pointee(DecoderBufferMatches(std::cref(*s16_buffer)))))
+      .WillOnce(DoAll(SaveArg<1>(&captured_sample_info),
+                      SaveArg<2>(&captured_buffer)));
+  base::OnceCallback<void(
+      DemuxerStream::Status status,
+      std::vector<scoped_refptr<::media::DecoderBuffer>> buffers)>
+      read_cb;
+  EXPECT_CALL(audio_stream_, OnRead).WillOnce(SaveArgByMove<0>(&read_cb));
+
+  DemuxerStreamReader stream_reader(
+      &audio_stream_, /*video_stream=*/nullptr, expected_info.audio_sample_info,
+      /*video_sample_info=*/std::nullopt,
+      base::BindLambdaForTesting(handle_buffer_cb_.AsStdFunction()),
+      base::BindLambdaForTesting(handle_eos_cb_.AsStdFunction()),
+      &renderer_client_);
+  stream_reader.ReadBuffer(kSeekTicket,
+                           StarboardMediaType::kStarboardMediaTypeAudio);
+
+  // Simulate the DemuxerStream providing a buffer.
+  ASSERT_FALSE(read_cb.is_null());
+  std::move(read_cb).Run(DemuxerStream::Status::kOk, {buffer});
+
+  ASSERT_THAT(captured_buffer, NotNull());
+
+  // Update the expectations to match the buffer that was returned from the
+  // DemuxerStreamReader. That info needs to be match the info in the starboard
+  // structs, so that the data passed to starboard has its lifetime managed
+  // properly.
+  expected_info.buffer = captured_buffer->data();
+  expected_info.buffer_size = captured_buffer->size();
+  EXPECT_THAT(captured_sample_info, MatchesStarboardSampleInfo(expected_info));
+}
+
+TEST_F(DemuxerStreamReaderTest,
+       ReadsVideoBufferButDoesNotCallCbIfDrmKeyUnavailable) {
+  constexpr int kSeekTicket = 7;
+  scoped_refptr<DecoderBuffer> buffer = DecoderBuffer::CopyFrom(kBufferData);
+  buffer->set_decrypt_config(DecryptConfig::CreateCencConfig(
+      std::string(kIdentifier), std::string(kIv),
+      /*subsamples=*/{}));
+
+  StarboardDrmSampleInfo drm_info;
+  drm_info.encryption_scheme =
+      StarboardDrmEncryptionScheme::kStarboardDrmEncryptionSchemeAesCtr;
+  // CENC encryption scheme does not use an encryption pattern.
+  drm_info.encryption_pattern.crypt_byte_block = 0;
+  drm_info.encryption_pattern.skip_byte_block = 0;
+  base::span<uint8_t>(drm_info.initialization_vector)
+      .first<kIv.size()>()
+      .copy_from(base::as_byte_span(kIv));
+  drm_info.initialization_vector_size = kIv.size();
+  base::span<uint8_t>(drm_info.identifier)
+      .first<kIdentifier.size()>()
+      .copy_from(base::as_byte_span(kIdentifier));
+  drm_info.identifier_size = kIdentifier.size();
+  drm_info.subsample_mapping = base::span_from_ref(kSubsampleMapping);
+
+  StarboardSampleInfo expected_info = CreateVideoSample(*buffer, &drm_info);
+
+  EXPECT_CALL(handle_eos_cb_, Call).Times(0);
+  // This should not be called, since the DRM key is not available.
+  EXPECT_CALL(handle_buffer_cb_, Call).Times(0);
+  base::OnceCallback<void(
+      DemuxerStream::Status status,
+      std::vector<scoped_refptr<::media::DecoderBuffer>> buffers)>
+      read_cb;
+  EXPECT_CALL(video_stream_, OnRead).WillOnce(SaveArgByMove<0>(&read_cb));
+
+  DemuxerStreamReader stream_reader(
+      /*audio_stream=*/nullptr, &video_stream_,
+      /*audio_sample_info=*/std::nullopt, expected_info.video_sample_info,
+      base::BindLambdaForTesting(handle_buffer_cb_.AsStdFunction()),
+      base::BindLambdaForTesting(handle_eos_cb_.AsStdFunction()),
+      &renderer_client_);
+  stream_reader.ReadBuffer(kSeekTicket,
+                           StarboardMediaType::kStarboardMediaTypeVideo);
+
+  // Simulate the DemuxerStream providing a buffer.
+  ASSERT_FALSE(read_cb.is_null());
+  std::move(read_cb).Run(DemuxerStream::Status::kOk, {buffer});
+}
+
+TEST_F(DemuxerStreamReaderTest,
+       ReadsVideoBufferAndCallsCbWhenDrmKeyIsAvailable) {
+  constexpr int kSeekTicket = 7;
+  scoped_refptr<DecoderBuffer> buffer = DecoderBuffer::CopyFrom(kBufferData);
+  buffer->set_decrypt_config(DecryptConfig::CreateCencConfig(
+      std::string(kIdentifier), std::string(kIv),
+      /*subsamples=*/{}));
+
+  StarboardDrmSampleInfo drm_info;
+  drm_info.encryption_scheme =
+      StarboardDrmEncryptionScheme::kStarboardDrmEncryptionSchemeAesCtr;
+  // CENC encryption scheme does not use an encryption pattern.
+  drm_info.encryption_pattern.crypt_byte_block = 0;
+  drm_info.encryption_pattern.skip_byte_block = 0;
+  base::span<uint8_t>(drm_info.initialization_vector)
+      .first<kIv.size()>()
+      .copy_from(base::as_byte_span(kIv));
+  drm_info.initialization_vector_size = kIv.size();
+  base::span<uint8_t>(drm_info.identifier)
+      .first<kIdentifier.size()>()
+      .copy_from(base::as_byte_span(kIdentifier));
+  drm_info.identifier_size = kIdentifier.size();
+  drm_info.subsample_mapping = base::span_from_ref(kSubsampleMapping);
+
+  StarboardSampleInfo expected_info = CreateVideoSample(*buffer, &drm_info);
+
+  EXPECT_CALL(handle_eos_cb_, Call).Times(0);
+  // This should be called once the DRM key is available.
+  EXPECT_CALL(handle_buffer_cb_,
+              Call(kSeekTicket, MatchesStarboardSampleInfo(expected_info),
+                   Pointee(DecoderBufferMatches(std::cref(*buffer)))))
+      .Times(1);
+  base::OnceCallback<void(
+      DemuxerStream::Status status,
+      std::vector<scoped_refptr<::media::DecoderBuffer>> buffers)>
+      read_cb;
+  EXPECT_CALL(video_stream_, OnRead).WillOnce(SaveArgByMove<0>(&read_cb));
+
+  DemuxerStreamReader stream_reader(
+      /*audio_stream=*/nullptr, &video_stream_,
+      /*audio_sample_info=*/std::nullopt, expected_info.video_sample_info,
+      base::BindLambdaForTesting(handle_buffer_cb_.AsStdFunction()),
+      base::BindLambdaForTesting(handle_eos_cb_.AsStdFunction()),
+      &renderer_client_);
+  stream_reader.ReadBuffer(kSeekTicket,
+                           StarboardMediaType::kStarboardMediaTypeVideo);
+
+  // Simulate the DemuxerStream providing a buffer.
+  ASSERT_FALSE(read_cb.is_null());
+  std::move(read_cb).Run(DemuxerStream::Status::kOk, {buffer});
+
+  // Simulate the DRM key being available. This should trigger the call to
+  // handle_buffer_cb_.
+  StarboardDrmKeyTracker::GetInstance().AddKey(std::string(kIdentifier),
+                                               "some_session");
+
+  // Use a run loop here, since the DRM key callback may be posted to a separate
+  // task.
+  base::RunLoop run_loop;
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE, run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/renderer/geometry_change_handler.cc b/chromecast/starboard/media/renderer/geometry_change_handler.cc
new file mode 100644
index 0000000..e683aab
--- /dev/null
+++ b/chromecast/starboard/media/renderer/geometry_change_handler.cc
@@ -0,0 +1,89 @@
+// 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 "chromecast/starboard/media/renderer/geometry_change_handler.h"
+
+#include "base/check.h"
+#include "base/logging.h"
+#include "chromecast/media/service/video_geometry_setter_service.h"
+#include "ui/display/screen.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+// Sets the SbPlayer's bounds as specified. `starboard` and `sb_player` must not
+// be null.
+void SetPlayerBounds(const gfx::RectF& bounds,
+                     StarboardApiWrapper* starboard,
+                     void* sb_player) {
+  CHECK(starboard);
+  CHECK(sb_player);
+
+  LOG(INFO) << "Setting SbPlayer's bounds to z=0, x=" << bounds.x()
+            << ", y=" << bounds.y() << ", width=" << bounds.width()
+            << ", height=" << bounds.height();
+  starboard->SetPlayerBounds(
+      sb_player, /*z_index=*/0, static_cast<int>(bounds.x()),
+      static_cast<int>(bounds.y()), static_cast<int>(bounds.width()),
+      static_cast<int>(bounds.height()));
+}
+
+}  // namespace
+
+GeometryChangeHandler::GeometryChangeHandler(
+    VideoGeometrySetterService* geometry_setter_service,
+    StarboardApiWrapper* starboard,
+    const base::UnguessableToken& overlay_plane_id)
+    : starboard_(starboard) {
+  CHECK(starboard_);
+  CHECK(geometry_setter_service);
+  geometry_setter_service->GetVideoGeometryChangeSubscriber(
+      geometry_change_subscriber_remote_.BindNewPipeAndPassReceiver());
+  geometry_change_subscriber_remote_->SubscribeToVideoGeometryChange(
+      overlay_plane_id,
+      geometry_change_client_receiver_.BindNewPipeAndPassRemote(),
+      base::DoNothing());
+}
+
+GeometryChangeHandler::~GeometryChangeHandler() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void GeometryChangeHandler::SetSbPlayer(void* sb_player) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(sb_player);
+  sb_player_ = sb_player;
+
+  // Update the player's bounds.
+  if (current_geometry_.has_value()) {
+    // Use the bounds specified by a mojo call.
+    SetPlayerBounds(*current_geometry_, starboard_, sb_player_);
+  } else {
+    // Default to fullscreen.
+    const gfx::Size display_size =
+        display::Screen::GetScreen()->GetPrimaryDisplay().GetSizeInPixel();
+    SetPlayerBounds(
+        gfx::RectF(0, 0, display_size.width(), display_size.height()),
+        starboard_, sb_player_);
+  }
+}
+
+void GeometryChangeHandler::OnVideoGeometryChange(
+    const gfx::RectF& rect_f,
+    gfx::OverlayTransform transform) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (current_geometry_.has_value() && rect_f == *current_geometry_) {
+    return;
+  }
+
+  current_geometry_ = rect_f;
+  if (sb_player_) {
+    SetPlayerBounds(*current_geometry_, starboard_, sb_player_);
+  }
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/renderer/geometry_change_handler.h b/chromecast/starboard/media/renderer/geometry_change_handler.h
new file mode 100644
index 0000000..729ed79
--- /dev/null
+++ b/chromecast/starboard/media/renderer/geometry_change_handler.h
@@ -0,0 +1,75 @@
+// 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 CHROMECAST_STARBOARD_MEDIA_RENDERER_GEOMETRY_CHANGE_HANDLER_H_
+#define CHROMECAST_STARBOARD_MEDIA_RENDERER_GEOMETRY_CHANGE_HANDLER_H_
+
+#include <optional>
+
+#include "base/sequence_checker.h"
+#include "chromecast/media/service/mojom/video_geometry_setter.mojom.h"
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/overlay_transform.h"
+
+namespace chromecast {
+namespace media {
+
+class VideoGeometrySetterService;
+
+// Receives notifications of geometry changes and sets an SbPlayer's bounds
+// accordingly. If no SbPlayer is available when a geometry change occurs, the
+// player's bounds will be set once an SbPlayer is assigned via SetSbPlayer.
+//
+// If no bounds have been set by the VideoGeometrySetterService, this class will
+// use the display resolution to set the bounds to fullscreen by default.
+//
+// A GeometryChangeHandler must be used on a single sequence.
+class GeometryChangeHandler : public mojom::VideoGeometryChangeClient {
+ public:
+  // `geometry_setter_service` and `starboard` must outlive this object, and
+  // cannot be null.
+  GeometryChangeHandler(VideoGeometrySetterService* geometry_setter_service,
+                        StarboardApiWrapper* starboard,
+                        const base::UnguessableToken& overlay_plane_id);
+
+  // Disallow copy and assign.
+  GeometryChangeHandler(const GeometryChangeHandler&) = delete;
+  GeometryChangeHandler& operator=(const GeometryChangeHandler&) = delete;
+
+  ~GeometryChangeHandler() override;
+
+  // Sets the SbPlayer that GeometryChangeHandler will notify of bounds changes.
+  // SbPlayerSetBounds will be called immediately. If a bounds change was
+  // pending, that geometry will be used. Otherwise, the bounds will be set to
+  // fullscreen.
+  //
+  // `sb_player` must not be null.
+  void SetSbPlayer(void* sb_player);
+
+  // mojom::VideoGeometryChangeClient implementation.
+  void OnVideoGeometryChange(const gfx::RectF& rect_f,
+                             gfx::OverlayTransform transform) override;
+
+ private:
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  mojo::Remote<mojom::VideoGeometryChangeSubscriber>
+      geometry_change_subscriber_remote_;
+  mojo::Receiver<mojom::VideoGeometryChangeClient>
+      geometry_change_client_receiver_{this};
+
+  StarboardApiWrapper* starboard_ = nullptr;
+  // This is nullopt if a geometry has not yet been set.
+  std::optional<gfx::RectF> current_geometry_;
+
+  void* sb_player_ = nullptr;
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_STARBOARD_MEDIA_RENDERER_GEOMETRY_CHANGE_HANDLER_H_
diff --git a/chromecast/starboard/media/renderer/geometry_change_handler_test.cc b/chromecast/starboard/media/renderer/geometry_change_handler_test.cc
new file mode 100644
index 0000000..f9ae35a
--- /dev/null
+++ b/chromecast/starboard/media/renderer/geometry_change_handler_test.cc
@@ -0,0 +1,152 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/starboard/media/renderer/geometry_change_handler.h"
+
+#include "base/run_loop.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/test/task_environment.h"
+#include "base/unguessable_token.h"
+#include "chromecast/media/service/video_geometry_setter_service.h"
+#include "chromecast/starboard/media/media/mock_starboard_api_wrapper.h"
+#include "mojo/core/embedder/embedder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/test/test_screen.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/overlay_transform.h"
+
+namespace chromecast {
+namespace media {
+namespace {
+
+using ::testing::InSequence;
+
+// Runs any pending tasks that have been posted to the current sequence.
+void RunPendingTasks() {
+  base::RunLoop run_loop;
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE, run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+TEST(GeometryChangeHandlerTest, ReadsBoundsFromVideoGeometrySetterService) {
+  base::test::TaskEnvironment task_environment;
+  mojo::core::Init();
+  display::test::TestScreen test_screen(/*create_display=*/true,
+                                        /*register_screen=*/true);
+
+  VideoGeometrySetterService geometry_setter_service;
+  MockStarboardApiWrapper starboard;
+
+  const auto plane_id = base::UnguessableToken::Create();
+
+  const gfx::RectF geometry(0, 0, 1920, 1080);
+  const gfx::OverlayTransform transform =
+      gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE;
+  // This will be used as an opaque ptr; its value does not matter.
+  int sb_player = 7;
+
+  EXPECT_CALL(starboard,
+              SetPlayerBounds(&sb_player, 0, static_cast<int>(geometry.x()),
+                              static_cast<int>(geometry.y()),
+                              static_cast<int>(geometry.width()),
+                              static_cast<int>(geometry.height())))
+      .Times(1);
+
+  GeometryChangeHandler handler(&geometry_setter_service, &starboard, plane_id);
+  RunPendingTasks();
+
+  static_cast<mojom::VideoGeometrySetter*>(&geometry_setter_service)
+      ->SetVideoGeometry(geometry, transform, plane_id);
+  RunPendingTasks();
+  handler.SetSbPlayer(&sb_player);
+}
+
+TEST(GeometryChangeHandlerTest,
+     ForwardsUpdatesFromVideoGeometrySetterServiceToStarboard) {
+  base::test::TaskEnvironment task_environment;
+  mojo::core::Init();
+  display::test::TestScreen test_screen(/*create_display=*/true,
+                                        /*register_screen=*/true);
+
+  VideoGeometrySetterService geometry_setter_service;
+  MockStarboardApiWrapper starboard;
+
+  const auto plane_id = base::UnguessableToken::Create();
+
+  const gfx::RectF geometry_1(0, 0, 1920, 1080);
+  const gfx::RectF geometry_2(0, 0, 720, 1280);
+  const gfx::OverlayTransform transform =
+      gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE;
+  // This will be used as an opaque ptr; its value does not matter.
+  int sb_player = 7;
+
+  {
+    InSequence s;
+    EXPECT_CALL(starboard,
+                SetPlayerBounds(&sb_player, 0, static_cast<int>(geometry_1.x()),
+                                static_cast<int>(geometry_1.y()),
+                                static_cast<int>(geometry_1.width()),
+                                static_cast<int>(geometry_1.height())))
+        .Times(1);
+
+    EXPECT_CALL(starboard,
+                SetPlayerBounds(&sb_player, 0, static_cast<int>(geometry_2.x()),
+                                static_cast<int>(geometry_2.y()),
+                                static_cast<int>(geometry_2.width()),
+                                static_cast<int>(geometry_2.height())))
+        .Times(1);
+  }
+
+  GeometryChangeHandler handler(&geometry_setter_service, &starboard, plane_id);
+  RunPendingTasks();
+
+  static_cast<mojom::VideoGeometrySetter*>(&geometry_setter_service)
+      ->SetVideoGeometry(geometry_1, transform, plane_id);
+  RunPendingTasks();
+  handler.SetSbPlayer(&sb_player);
+
+  static_cast<mojom::VideoGeometrySetter*>(&geometry_setter_service)
+      ->SetVideoGeometry(geometry_2, transform, plane_id);
+  RunPendingTasks();
+}
+
+TEST(GeometryChangeHandlerTest, ReadsBoundsFromScreenResolution) {
+  base::test::TaskEnvironment task_environment;
+  mojo::core::Init();
+  display::test::TestScreen test_screen(/*create_display=*/true,
+                                        /*register_screen=*/true);
+
+  VideoGeometrySetterService geometry_setter_service;
+  MockStarboardApiWrapper starboard;
+
+  const auto plane_id = base::UnguessableToken::Create();
+
+  // This will be used as an opaque ptr; its value does not matter.
+  int sb_player = 7;
+
+  // Since a resolution has not been set, the bounds should be set to
+  // fullscreen.
+  EXPECT_CALL(
+      starboard,
+      SetPlayerBounds(
+          &sb_player, 0,
+          static_cast<int>(display::test::TestScreen::kDefaultScreenBounds.x()),
+          static_cast<int>(display::test::TestScreen::kDefaultScreenBounds.y()),
+          static_cast<int>(
+              display::test::TestScreen::kDefaultScreenBounds.width()),
+          static_cast<int>(
+              display::test::TestScreen::kDefaultScreenBounds.height())))
+      .Times(1);
+
+  GeometryChangeHandler handler(&geometry_setter_service, &starboard, plane_id);
+  RunPendingTasks();
+  handler.SetSbPlayer(&sb_player);
+  RunPendingTasks();
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/renderer/starboard_player_manager.cc b/chromecast/starboard/media/renderer/starboard_player_manager.cc
new file mode 100644
index 0000000..7ac1564
--- /dev/null
+++ b/chromecast/starboard/media/renderer/starboard_player_manager.cc
@@ -0,0 +1,353 @@
+// 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 "chromecast/starboard/media/renderer/starboard_player_manager.h"
+
+#include "base/check.h"
+#include "base/containers/span.h"
+#include "base/functional/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "chromecast/starboard/media/cdm/starboard_drm_wrapper.h"
+#include "chromecast/starboard/media/media/drm_util.h"
+#include "chromecast/starboard/media/renderer/chromium_starboard_conversions.h"
+
+namespace chromecast {
+namespace media {
+
+std::unique_ptr<StarboardPlayerManager> StarboardPlayerManager::Create(
+    StarboardApiWrapper* starboard,
+    ::media::DemuxerStream* audio_stream,
+    ::media::DemuxerStream* video_stream,
+    ::media::RendererClient* client,
+    scoped_refptr<base::SequencedTaskRunner> media_task_runner,
+    bool enable_buffering) {
+  if ((!audio_stream && !video_stream) || !starboard || !client ||
+      !media_task_runner) {
+    return nullptr;
+  }
+
+  // These objects need to outlive the call to CreatePlayer, since
+  // creation_param might reference pointers derived from vectors stored in
+  // these objects (for extra_data).
+  ::media::AudioDecoderConfig audio_config;
+  ::media::VideoDecoderConfig video_config;
+
+  std::optional<StarboardAudioSampleInfo> audio_sample_info;
+  std::optional<StarboardVideoSampleInfo> video_sample_info;
+
+  chromecast::media::StarboardPlayerCreationParam creation_param = {};
+  creation_param.drm_system = StarboardDrmWrapper::GetInstance().GetDrmSystem();
+  creation_param.output_mode =
+      StarboardPlayerOutputMode::kStarboardPlayerOutputModePunchOut;
+
+  if (audio_stream) {
+    audio_stream->EnableBitstreamConverter();
+    audio_config = audio_stream->audio_decoder_config();
+    audio_sample_info = ToStarboardAudioSampleInfo(audio_config);
+    if (!audio_sample_info) {
+      LOG(ERROR) << "Invalid or unsupported audio config: "
+                 << audio_config.AsHumanReadableString();
+      return nullptr;
+    }
+
+    LOG(INFO) << "Initial audio config: "
+              << audio_config.AsHumanReadableString();
+    creation_param.audio_sample_info = *audio_sample_info;
+  }
+
+  if (video_stream) {
+    // Convert to H264 and HEVC content to annex-b form, since that's the form
+    // that Starboard requires.
+    video_stream->EnableBitstreamConverter();
+    video_config = video_stream->video_decoder_config();
+    video_sample_info = ToStarboardVideoSampleInfo(video_config);
+    if (!video_sample_info) {
+      LOG(ERROR) << "Invalid or unsupported video config: "
+                 << video_config.AsHumanReadableString();
+      return nullptr;
+    }
+
+    LOG(INFO) << "Initial video config: "
+              << video_config.AsHumanReadableString();
+    creation_param.video_sample_info = *video_sample_info;
+
+    if (!enable_buffering) {
+      // Note: this is not part of the official starboard API. We are using this
+      // arbitrary string value to inform the starboard impl that they should
+      // prioritize minimizing latency (render the frames as soon as possible).
+      creation_param.video_sample_info.max_video_capabilities = "streaming=1";
+    }
+  }
+
+  // base::WrapUnique is necessary because we're calling a private ctor.
+  auto starboard_player_manager = base::WrapUnique(new StarboardPlayerManager(
+      starboard, audio_stream, video_stream, std::move(audio_sample_info),
+      std::move(video_sample_info), client, std::move(media_task_runner)));
+
+  starboard->EnsureInitialized();
+  void* sb_player = starboard->CreatePlayer(
+      &creation_param, &starboard_player_manager->callback_handler_);
+
+  if (!sb_player) {
+    LOG(ERROR) << "Could not create SbPlayer";
+    return nullptr;
+  }
+  starboard_player_manager->player_ = sb_player;
+  return starboard_player_manager;
+}
+
+StarboardPlayerManager::StarboardPlayerManager(
+    StarboardApiWrapper* starboard,
+    ::media::DemuxerStream* audio_stream,
+    ::media::DemuxerStream* video_stream,
+    std::optional<StarboardAudioSampleInfo> audio_sample_info,
+    std::optional<StarboardVideoSampleInfo> video_sample_info,
+    ::media::RendererClient* client,
+    scoped_refptr<base::SequencedTaskRunner> media_task_runner)
+    :  // base::Unretained(this) is safe here because demuxer_stream_reader_
+       // will be destroyed before `this`.
+      starboard_(starboard),
+      client_(client),
+      stats_tracker_(client),
+      task_runner_(std::move(media_task_runner)),
+      demuxer_stream_reader_(
+          audio_stream,
+          video_stream,
+          std::move(audio_sample_info),
+          std::move(video_sample_info),
+          /*handle_buffer_cb=*/
+          base::BindRepeating(&StarboardPlayerManager::PushBuffer,
+                              base::Unretained(this)),
+          base::BindRepeating(&StarboardPlayerManager::PushEos,
+                              base::Unretained(this)),
+          client_) {
+  CHECK(starboard_);
+  CHECK(client_);
+  CHECK(task_runner_);
+  // player_ is set later in the factory function to create
+  // StarboardPlayerManager.
+}
+
+StarboardPlayerManager::~StarboardPlayerManager() {
+  CHECK(task_runner_->RunsTasksInCurrentSequence());
+
+  if (player_) {
+    starboard_->DestroyPlayer(player_);
+  }
+}
+
+void StarboardPlayerManager::PushBuffer(
+    int seek_ticket,
+    StarboardSampleInfo sample_info,
+    scoped_refptr<::media::DecoderBuffer> buffer) {
+  CHECK(player_);
+  if (seek_ticket != seek_ticket_) {
+    LOG(INFO) << "Ignoring buffer for old seek ticket (expected "
+              << seek_ticket_ << ", got " << seek_ticket << ")";
+    return;
+  }
+
+  starboard_->WriteSample(player_,
+                          static_cast<StarboardMediaType>(sample_info.type),
+                          base::span_from_ref(sample_info));
+  CHECK(addr_to_buffer_.insert({sample_info.buffer, std::move(buffer)}).second)
+      << "Attempted to insert a buffer that already exists, at address: "
+      << sample_info.buffer;
+
+  UpdateStats(sample_info);
+}
+
+void StarboardPlayerManager::UpdateStats(
+    const StarboardSampleInfo& sample_info) {
+  StarboardPlayerInfo player_info = {};
+  starboard_->GetPlayerInfo(player_, &player_info);
+
+  stats_tracker_.UpdateStats(player_info, sample_info);
+}
+
+void StarboardPlayerManager::PushEos(int seek_ticket, StarboardMediaType type) {
+  CHECK(player_);
+  if (seek_ticket != seek_ticket_) {
+    LOG(INFO) << "Ignoring end of stream for old seek ticket (expected "
+              << seek_ticket_ << ", got " << seek_ticket << ")";
+    return;
+  }
+  starboard_->WriteEndOfStream(player_, type);
+}
+
+void StarboardPlayerManager::StartPlayingFrom(base::TimeDelta time) {
+  CHECK(task_runner_->RunsTasksInCurrentSequence());
+  CHECK(player_);
+  LOG(INFO) << "StartPlayingFrom: " << time;
+  flushing_ = false;
+  LOG(INFO) << "Setting playback rate to " << playback_rate_;
+  // In case this is the first call to StartPlayingFrom, or if this is called
+  // after a flush, ensure that we have the correct rate set before seeking.
+  starboard_->SetPlaybackRate(player_, playback_rate_);
+  starboard_->SeekTo(player_, time.InMicroseconds(), ++seek_ticket_);
+}
+
+void StarboardPlayerManager::Flush() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  CHECK(player_);
+  LOG(INFO) << "StarboardPlayerManager::Flush";
+  flushing_ = true;
+  // Setting the playback rate to 0 pauses playback.
+  starboard_->SetPlaybackRate(player_, 0.0);
+
+  StarboardPlayerInfo player_info = {};
+  starboard_->GetPlayerInfo(player_, &player_info);
+
+  // Seeking causes starboard to flush its pipeline.
+  starboard_->SeekTo(player_, player_info.current_media_timestamp_micros,
+                     ++seek_ticket_);
+}
+
+void StarboardPlayerManager::SetPlaybackRate(double playback_rate) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  CHECK(player_);
+  LOG(INFO) << "SetPlaybackRate: " << playback_rate;
+  playback_rate_ = playback_rate;
+  starboard_->SetPlaybackRate(player_, playback_rate);
+}
+
+void StarboardPlayerManager::SetVolume(float volume) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  CHECK(player_);
+  LOG(INFO) << "StarboardPlayerManager::SetVolume: " << volume;
+  starboard_->SetVolume(player_, volume);
+}
+
+base::TimeDelta StarboardPlayerManager::GetMediaTime() {
+  CHECK(task_runner_->RunsTasksInCurrentSequence());
+  CHECK(player_);
+  StarboardPlayerInfo player_info = {};
+  starboard_->GetPlayerInfo(player_, &player_info);
+  return base::Microseconds(player_info.current_media_timestamp_micros);
+}
+
+void* StarboardPlayerManager::GetSbPlayer() {
+  return player_;
+}
+
+void StarboardPlayerManager::OnDecoderStatus(
+    void* player,
+    StarboardMediaType type,
+    StarboardDecoderState decoder_state,
+    int ticket) {
+  if (!task_runner_->RunsTasksInCurrentSequence()) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&StarboardPlayerManager::OnDecoderStatus,
+                                  weak_factory_.GetWeakPtr(), player, type,
+                                  decoder_state, ticket));
+    return;
+  }
+
+  if (flushing_) {
+    LOG(INFO) << "Ignoring call for data from Starboard, because the pipeline "
+                 "is flushing.";
+    return;
+  }
+  if (ticket != seek_ticket_) {
+    LOG(INFO) << "Ignoring call for data from Starboard, because the seek "
+                 "ticket does not match ("
+              << ticket << " vs expected " << seek_ticket_ << ")";
+    return;
+  }
+
+  demuxer_stream_reader_.ReadBuffer(seek_ticket_, type);
+}
+
+void StarboardPlayerManager::DeallocateSample(void* player,
+                                              const void* sample_buffer) {
+  if (!task_runner_->RunsTasksInCurrentSequence()) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&StarboardPlayerManager::DeallocateSample,
+                       weak_factory_.GetWeakPtr(), player, sample_buffer));
+    return;
+  }
+
+  addr_to_buffer_.erase(sample_buffer);
+}
+
+void StarboardPlayerManager::OnPlayerStatus(
+    void* player,
+    chromecast::media::StarboardPlayerState state,
+    int ticket) {
+  if (!task_runner_->RunsTasksInCurrentSequence()) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&StarboardPlayerManager::OnPlayerStatus,
+                       weak_factory_.GetWeakPtr(), player, state, ticket));
+    return;
+  }
+
+  DCHECK_EQ(player, player_);
+  LOG(INFO) << "Received SbPlayer state: " << state;
+  if (state == StarboardPlayerState::kStarboardPlayerStateEndOfStream) {
+    client_->OnEnded();
+  } else if (state == StarboardPlayerState::kStarboardPlayerStatePresenting) {
+    client_->OnBufferingStateChange(
+        ::media::BufferingState::BUFFERING_HAVE_ENOUGH,
+        ::media::BufferingStateChangeReason::BUFFERING_CHANGE_REASON_UNKNOWN);
+  }
+}
+
+void StarboardPlayerManager::OnPlayerError(
+    void* player,
+    chromecast::media::StarboardPlayerError error,
+    std::string message) {
+  if (!task_runner_->RunsTasksInCurrentSequence()) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&StarboardPlayerManager::OnPlayerError,
+                       weak_factory_.GetWeakPtr(), player, error, message));
+    return;
+  }
+
+  DCHECK_EQ(player, player_);
+  LOG(ERROR) << "Received SbPlayer error " << error
+             << ", with message: " << message;
+  client_->OnError(::media::PIPELINE_ERROR_COULD_NOT_RENDER);
+}
+
+void StarboardPlayerManager::CallOnDecoderStatus(
+    void* player,
+    void* context,
+    chromecast::media::StarboardMediaType type,
+    chromecast::media::StarboardDecoderState decoder_state,
+    int ticket) {
+  reinterpret_cast<StarboardPlayerManager*>(context)->OnDecoderStatus(
+      player, type, decoder_state, ticket);
+}
+
+void StarboardPlayerManager::CallDeallocateSample(void* player,
+                                                  void* context,
+                                                  const void* sample_buffer) {
+  reinterpret_cast<StarboardPlayerManager*>(context)->DeallocateSample(
+      player, sample_buffer);
+}
+
+void StarboardPlayerManager::CallOnPlayerStatus(
+    void* player,
+    void* context,
+    chromecast::media::StarboardPlayerState state,
+    int ticket) {
+  reinterpret_cast<StarboardPlayerManager*>(context)->OnPlayerStatus(
+      player, state, ticket);
+}
+
+void StarboardPlayerManager::CallOnPlayerError(
+    void* player,
+    void* context,
+    chromecast::media::StarboardPlayerError error,
+    std::string message) {
+  reinterpret_cast<StarboardPlayerManager*>(context)->OnPlayerError(
+      player, error, message);
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/starboard/media/renderer/starboard_player_manager.h b/chromecast/starboard/media/renderer/starboard_player_manager.h
new file mode 100644
index 0000000..e87be57
--- /dev/null
+++ b/chromecast/starboard/media/renderer/starboard_player_manager.h
@@ -0,0 +1,173 @@
+// 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 CHROMECAST_STARBOARD_MEDIA_RENDERER_STARBOARD_PLAYER_MANAGER_H_
+#define CHROMECAST_STARBOARD_MEDIA_RENDERER_STARBOARD_PLAYER_MANAGER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/functional/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "chromecast/starboard/media/renderer/client_stats_tracker.h"
+#include "chromecast/starboard/media/renderer/demuxer_stream_reader.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/renderer_client.h"
+
+namespace chromecast {
+namespace media {
+
+// Manages interactions with an SbPlayer. In particular, this class has several
+// responsibilities:
+//
+// * Reading buffers from DemuxerStreams (this logic is encapsulated in a
+//    separate class, DemuxerStreamReader, which is used by this one)
+// * Providing those buffers to Starboard as they are requested
+// * Deallocating those buffers once they are no longer needed
+// * Notifying RendererClient when certain events occur
+// * Updating RendererClient stats (this logic is encapsulated in a separate
+//    class, ClientStatsTracker, which is used by this one)
+//
+// Instances of StarboardPlayerManager should be created via Create().
+//
+// Public functions must be called on the media sequence matching the runner
+// which is provided to Create.
+class StarboardPlayerManager {
+ public:
+  // Factory function for creating a StarboardPlayerManager. One of
+  // `audio_stream` or `video_stream` may be null (but not both). All other args
+  // must not be null.
+  //
+  // If an SbPlayer cannot be created, or if any of the conditions above are not
+  // met, null will be returned.
+  static std::unique_ptr<StarboardPlayerManager> Create(
+      StarboardApiWrapper* starboard,
+      ::media::DemuxerStream* audio_stream,
+      ::media::DemuxerStream* video_stream,
+      ::media::RendererClient* client,
+      scoped_refptr<base::SequencedTaskRunner> media_task_runner,
+      bool enable_buffering);
+
+  // Disallow copy and assign.
+  StarboardPlayerManager(const StarboardPlayerManager&) = delete;
+  StarboardPlayerManager& operator=(const StarboardPlayerManager&) = delete;
+
+  ~StarboardPlayerManager();
+
+  // Begins playback from `time`. This triggers a seek in starboard, which will
+  // cause starboard to start requesting buffers. A request for buffers from
+  // starboard in turn triggers a call to DemuxerStream::Read via
+  // DemuxerStreamReader.
+  void StartPlayingFrom(base::TimeDelta time);
+
+  // Discards any pending buffers and pauses playback.
+  void Flush();
+
+  // Sets the playback rate. 0 means that playback is paused.
+  void SetPlaybackRate(double playback_rate);
+
+  // Sets the media volume. This is different from the system volume; it is
+  // essentially a multiplier, e.g. for fade in/out effects. Most cast apps do
+  // not use this.
+  void SetVolume(float volume);
+
+  // Returns the current media time.
+  base::TimeDelta GetMediaTime();
+
+  // Returns the SbPlayer owned by this object.
+  void* GetSbPlayer();
+
+ private:
+  explicit StarboardPlayerManager(
+      StarboardApiWrapper* starboard,
+      ::media::DemuxerStream* audio_stream,
+      ::media::DemuxerStream* video_stream,
+      std::optional<StarboardAudioSampleInfo> audio_sample_info,
+      std::optional<StarboardVideoSampleInfo> video_sample_info,
+      ::media::RendererClient* client,
+      scoped_refptr<base::SequencedTaskRunner> media_task_runner);
+
+  // Pushes `buffer` to starboard.
+  void PushBuffer(int seek_ticket,
+                  StarboardSampleInfo sample_info,
+                  scoped_refptr<::media::DecoderBuffer> buffer);
+
+  // Signals to starboard that the end of a stream has been reached (for type
+  // `type`).
+  void PushEos(int seek_ticket, StarboardMediaType type);
+
+  // Updates the client's stats based on an audio/video buffer being pushed.
+  void UpdateStats(const StarboardSampleInfo& sample_info);
+
+  // Called by Starboard when a decoder's status changes.
+  void OnDecoderStatus(void* player,
+                       StarboardMediaType type,
+                       StarboardDecoderState decoder_state,
+                       int ticket);
+
+  // Called by Starboard when a buffer can safely be deallocated.
+  void DeallocateSample(void* player, const void* sample_buffer);
+
+  // Called by Starboard when the player's state changes.
+  void OnPlayerStatus(void* player, StarboardPlayerState state, int ticket);
+
+  // Called by Starboard when a player-related error has occurred.
+  void OnPlayerError(void* player,
+                     StarboardPlayerError error,
+                     std::string message);
+
+  // Callbacks called by Starboard. These simply call the private methods
+  // declared above.
+  static void CallOnDecoderStatus(void* player,
+                                  void* context,
+                                  StarboardMediaType type,
+                                  StarboardDecoderState decoder_state,
+                                  int ticket);
+  static void CallDeallocateSample(void* player,
+                                   void* context,
+                                   const void* sample_buffer);
+  static void CallOnPlayerStatus(void* player,
+                                 void* context,
+                                 StarboardPlayerState state,
+                                 int ticket);
+  static void CallOnPlayerError(void* player,
+                                void* context,
+                                StarboardPlayerError error,
+                                std::string message);
+
+  StarboardPlayerCallbackHandler callback_handler_{
+      this,
+      &StarboardPlayerManager::CallOnDecoderStatus,
+      &StarboardPlayerManager::CallDeallocateSample,
+      &StarboardPlayerManager::CallOnPlayerStatus,
+      &StarboardPlayerManager::CallOnPlayerError,
+  };
+  StarboardApiWrapper* starboard_ = nullptr;
+  // This class owns the SbPlayer.
+  void* player_ = nullptr;
+  ::media::RendererClient* client_ = nullptr;
+  ClientStatsTracker stats_tracker_;
+  bool flushing_ = false;
+  double playback_rate_ = 0.0;
+  // Buffers from an old seek ticket can be safely ignored.
+  int seek_ticket_ = 0;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  DemuxerStreamReader demuxer_stream_reader_;
+  // Maps from a buffer address to the scoped_refptr managing the buffer's
+  // lifetime.
+  base::flat_map<const void*, scoped_refptr<::media::DecoderBuffer>>
+      addr_to_buffer_;
+
+  // This should be destructed first, to invalidate any weak ptrs.
+  base::WeakPtrFactory<StarboardPlayerManager> weak_factory_{this};
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_STARBOARD_MEDIA_RENDERER_STARBOARD_PLAYER_MANAGER_H_
diff --git a/chromecast/starboard/media/renderer/starboard_player_manager_test.cc b/chromecast/starboard/media/renderer/starboard_player_manager_test.cc
new file mode 100644
index 0000000..9f287360
--- /dev/null
+++ b/chromecast/starboard/media/renderer/starboard_player_manager_test.cc
@@ -0,0 +1,731 @@
+// 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 "chromecast/starboard/media/renderer/starboard_player_manager.h"
+
+#include <array>
+#include <vector>
+
+#include "base/task/sequenced_task_runner.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "chromecast/starboard/media/cdm/starboard_drm_wrapper.h"
+#include "chromecast/starboard/media/media/mock_starboard_api_wrapper.h"
+#include "chromecast/starboard/media/media/starboard_api_wrapper.h"
+#include "chromecast/starboard/media/media/test_matchers.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/encryption_scheme.h"
+#include "media/base/mock_filters.h"
+#include "media/base/video_color_space.h"
+#include "media/base/video_transformation.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace chromecast {
+namespace media {
+namespace {
+
+using ::base::test::RunOnceCallback;
+using ::media::DemuxerStream;
+using ::media::MockDemuxerStream;
+using ::media::MockRendererClient;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::DoAll;
+using ::testing::DoubleEq;
+using ::testing::InSequence;
+using ::testing::IsNull;
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::Pointee;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::WithArg;
+
+// Returns a valid audio config with values arbitrarily set. The values will
+// match the values of GetStarboardAudioConfig.
+::media::AudioDecoderConfig GetChromiumAudioConfig() {
+  return ::media::AudioDecoderConfig(
+      ::media::AudioCodec::kAC3, ::media::SampleFormat::kSampleFormatS32,
+      ::media::ChannelLayout::CHANNEL_LAYOUT_5_1, 44100, /*extra_data=*/{},
+      ::media::EncryptionScheme::kUnencrypted);
+}
+
+// Returns a valid video config with values arbitrarily set. The values will
+// match the values of GetStarboardVideoConfig.
+::media::VideoDecoderConfig GetChromiumVideoConfig() {
+  ::media::VideoDecoderConfig video_config(
+      ::media::VideoCodec::kHEVC, ::media::VideoCodecProfile::HEVCPROFILE_MAIN,
+      ::media::VideoDecoderConfig::AlphaMode::kIsOpaque,
+      ::media::VideoColorSpace(1, 1, 1, gfx::ColorSpace::RangeID::LIMITED),
+      ::media::VideoTransformation(), gfx::Size(1920, 1080),
+      gfx::Rect(0, 0, 1919, 1079), gfx::Size(1280, 720), /*extra_data=*/{},
+      ::media::EncryptionScheme::kUnencrypted);
+  video_config.set_level(5);
+  return video_config;
+}
+
+// Returns a valid starboard audio config with values arbitrarily set. The
+// values will match the values of GetChromiumAudioConfig.
+StarboardAudioSampleInfo GetStarboardAudioConfig() {
+  return StarboardAudioSampleInfo{
+      .codec = kStarboardAudioCodecAc3,
+      .mime = R"-(audio/mp4; codecs="ac-3")-",
+      .format_tag = 0,
+      .number_of_channels = 6,
+      .samples_per_second = 44100,
+      .average_bytes_per_second = (32 / 8) * 44100 * 6,
+      .block_alignment = 4,
+      .bits_per_sample = 32,
+      .audio_specific_config_size = 0,
+      .audio_specific_config = nullptr,
+  };
+}
+
+// Returns a valid starboard video config with values arbitrarily set. The
+// values will match the values of GetChromiumVideoConfig.
+StarboardVideoSampleInfo GetStarboardVideoConfig() {
+  return StarboardVideoSampleInfo{
+      .codec = kStarboardVideoCodecH265,
+      .mime = R"-(video/mp4; codecs="hev1.1.6.L5.B0")-",
+      .max_video_capabilities = "",
+      .is_key_frame = false,
+      .frame_width = 1920,
+      .frame_height = 1080,
+      .color_metadata =
+          StarboardColorMetadata{
+              // These 0 fields signify "unknown" to starboard.
+              .bits_per_channel = 0,
+              .chroma_subsampling_horizontal = 0,
+              .chroma_subsampling_vertical = 0,
+              .cb_subsampling_horizontal = 0,
+              .cb_subsampling_vertical = 0,
+              .chroma_siting_horizontal = 0,
+              .chroma_siting_vertical = 0,
+              // No HDR metadata, so everything is 0.
+              .mastering_metadata = StarboardMediaMasteringMetadata{},
+              .max_cll = 0,
+              .max_fall = 0,
+              .primaries = 1,  // BT.709
+              .transfer = 1,   // BT.709
+              .matrix = 1,     // BT.709
+              .range = 1,      // broadcast range
+              .custom_primary_matrix = {0},
+          },
+  };
+}
+
+// A test fixture is used to avoid boilerplate in each test (managing the
+// lifetime of the TaskEnvironment, creating mocks, etc).
+class StarboardPlayerManagerTest : public ::testing::Test {
+ protected:
+  StarboardPlayerManagerTest()
+      : audio_stream_(DemuxerStream::Type::AUDIO),
+        video_stream_(DemuxerStream::Type::VIDEO) {
+    ON_CALL(starboard_for_drm_, CreateDrmSystem)
+        .WillByDefault(Return(&drm_system_));
+    StarboardDrmWrapper::SetSingletonForTesting(&starboard_for_drm_);
+  }
+
+  ~StarboardPlayerManagerTest() override = default;
+
+  // This should be destructed last.
+  base::test::TaskEnvironment task_environment_;
+  NiceMock<MockStarboardApiWrapper> starboard_;
+
+  // It is undefined behavior to set expectations on a mock after its mock
+  // functions have been called. Thus, to be safe we use a separate mock
+  // starboard for the StarboardDrmWrapper. All expectations are set before it
+  // is passed to the StarboardDrmWrapper (in this fixture's ctor).
+  NiceMock<MockStarboardApiWrapper> starboard_for_drm_;
+  MockDemuxerStream audio_stream_;
+  MockDemuxerStream video_stream_;
+  MockRendererClient renderer_client_;
+
+  // Since SbPlayer is used as an opaque void* by cast, we can use any type
+  // here. All that matters is the address.
+  int sb_player_ = 1;
+  // Same for SbDrmSystem.
+  int drm_system_ = 2;
+};
+
+TEST_F(StarboardPlayerManagerTest,
+       EnablesBitstreamConvertersForDemuxerStreams) {
+  // Starboard requires bitstream formats, so it is important that this be
+  // configured properly.
+  EXPECT_CALL(audio_stream_, EnableBitstreamConverter);
+  EXPECT_CALL(video_stream_, EnableBitstreamConverter);
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = GetStarboardAudioConfig(),
+              .video_sample_info = GetStarboardVideoConfig(),
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(Return(&sb_player_));
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  EXPECT_THAT(
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true),
+      NotNull());
+}
+
+TEST_F(StarboardPlayerManagerTest, PlaybackStartCausesSeekInStarboard) {
+  constexpr auto kSeekTime = base::Seconds(10);
+
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = GetStarboardAudioConfig(),
+              .video_sample_info = GetStarboardVideoConfig(),
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(Return(&sb_player_));
+
+  EXPECT_CALL(starboard_, SeekTo(&sb_player_, kSeekTime.InMicroseconds(), _))
+      .Times(1);
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  std::unique_ptr<StarboardPlayerManager> player_manager =
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+
+  player_manager->StartPlayingFrom(kSeekTime);
+}
+
+TEST_F(StarboardPlayerManagerTest, FlushCausesSeekToCurrentTimeInStarboard) {
+  constexpr auto kSeekTime = base::Seconds(10);
+  constexpr auto kMediaTime = base::Seconds(12);
+
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = GetStarboardAudioConfig(),
+              .video_sample_info = GetStarboardVideoConfig(),
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(Return(&sb_player_));
+  EXPECT_CALL(starboard_, GetPlayerInfo(&sb_player_, NotNull()))
+      .WillOnce(WithArg<1>([kMediaTime](StarboardPlayerInfo* player_info) {
+        // player_info cannot be null due to the NotNull matcher, so no need to
+        // check for null here.
+        *player_info = {};
+        player_info->current_media_timestamp_micros =
+            kMediaTime.InMicroseconds();
+      }));
+
+  {
+    InSequence s;
+    // There should be two seeks: one when we start playback, and one when we
+    // flush (set to the current media time).
+    EXPECT_CALL(starboard_, SeekTo(&sb_player_, kSeekTime.InMicroseconds(), _))
+        .Times(1);
+    EXPECT_CALL(starboard_, SeekTo(&sb_player_, kMediaTime.InMicroseconds(), _))
+        .Times(1);
+  }
+
+  // Additionally, the playback rate should be set to 0 on flush.
+  EXPECT_CALL(starboard_, SetPlaybackRate(&sb_player_, DoubleEq(0)))
+      .Times(AtLeast(1));
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  std::unique_ptr<StarboardPlayerManager> player_manager =
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+
+  player_manager->StartPlayingFrom(kSeekTime);
+  player_manager->Flush();
+}
+
+TEST_F(StarboardPlayerManagerTest, ForwardsPlaybackRateChangesToStarboard) {
+  constexpr auto kSeekTime = base::Seconds(10);
+  constexpr double kPlaybackRate = 2.0;
+
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = GetStarboardAudioConfig(),
+              .video_sample_info = GetStarboardVideoConfig(),
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(Return(&sb_player_));
+
+  EXPECT_CALL(starboard_, SetPlaybackRate(&sb_player_, DoubleEq(0.0)))
+      .Times(AnyNumber());
+  EXPECT_CALL(starboard_, SetPlaybackRate(&sb_player_, DoubleEq(kPlaybackRate)))
+      .Times(1);
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  std::unique_ptr<StarboardPlayerManager> player_manager =
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+
+  player_manager->StartPlayingFrom(kSeekTime);
+  player_manager->SetPlaybackRate(kPlaybackRate);
+}
+
+TEST_F(StarboardPlayerManagerTest, ForwardsStreamVolumeChangesToStarboard) {
+  constexpr auto kSeekTime = base::Seconds(10);
+  constexpr float kVolume = 0.3;
+
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = GetStarboardAudioConfig(),
+              .video_sample_info = GetStarboardVideoConfig(),
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(Return(&sb_player_));
+
+  EXPECT_CALL(starboard_, SetVolume(&sb_player_, DoubleEq(kVolume))).Times(1);
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  std::unique_ptr<StarboardPlayerManager> player_manager =
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+
+  player_manager->StartPlayingFrom(kSeekTime);
+  player_manager->SetVolume(kVolume);
+}
+
+TEST_F(StarboardPlayerManagerTest, GetsCurrentMediaTimeFromStarboard) {
+  constexpr auto kSeekTime = base::Seconds(10);
+  constexpr auto kMediaTime = base::Seconds(11);
+
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = GetStarboardAudioConfig(),
+              .video_sample_info = GetStarboardVideoConfig(),
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(Return(&sb_player_));
+
+  EXPECT_CALL(starboard_, GetPlayerInfo(&sb_player_, NotNull()))
+      .WillOnce(WithArg<1>([kMediaTime](StarboardPlayerInfo* player_info) {
+        // player_info cannot be null due to the NotNull matcher, so no need to
+        // check for null here.
+        *player_info = {};
+        player_info->current_media_timestamp_micros =
+            kMediaTime.InMicroseconds();
+      }));
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  std::unique_ptr<StarboardPlayerManager> player_manager =
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+
+  player_manager->StartPlayingFrom(kSeekTime);
+  EXPECT_EQ(player_manager->GetMediaTime(), kMediaTime);
+}
+
+TEST_F(StarboardPlayerManagerTest, GetSbPlayerReturnsTheSbPlayer) {
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = GetStarboardAudioConfig(),
+              .video_sample_info = GetStarboardVideoConfig(),
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(Return(&sb_player_));
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  std::unique_ptr<StarboardPlayerManager> player_manager =
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+  EXPECT_EQ(player_manager->GetSbPlayer(), &sb_player_);
+}
+
+TEST_F(StarboardPlayerManagerTest,
+       BufferingDisabledSetsStreamingInMaxVideoCapabilities) {
+  // streaming=1 is not part of an official starboard API, but cast sets this
+  // field to signal to partners that their SbPlayer should prioritize
+  // minimizing latency (e.g. for when the user is mirroring to the cast
+  // device).
+  StarboardVideoSampleInfo sb_video_config = GetStarboardVideoConfig();
+  sb_video_config.max_video_capabilities = "streaming=1";
+
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = GetStarboardAudioConfig(),
+              .video_sample_info = sb_video_config,
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(Return(&sb_player_));
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  EXPECT_THAT(
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/false),
+      NotNull());
+}
+
+TEST_F(StarboardPlayerManagerTest,
+       ReadsFromDemuxerStreamsAndWritesBuffersToStarboard) {
+  constexpr auto kSeekTime = base::Seconds(10);
+  constexpr auto kVideoBufferTs = base::Milliseconds(10001);
+  constexpr auto kVideoData = std::to_array<uint8_t>({1, 2, 3, 4, 5});
+  constexpr auto kAudioBufferTs = base::Milliseconds(10002);
+  constexpr auto kAudioData = std::to_array<uint8_t>({9, 8, 7});
+
+  const StarboardAudioSampleInfo sb_audio_config = GetStarboardAudioConfig();
+  const StarboardVideoSampleInfo sb_video_config = GetStarboardVideoConfig();
+
+  // This will be updated whenever the player manager seeks in starboard.
+  int seek_ticket = -1;
+  ON_CALL(starboard_, SeekTo(&sb_player_, _, _))
+      .WillByDefault(SaveArg<2>(&seek_ticket));
+
+  // This will be set to the callbacks received by the mock Starboard.
+  const StarboardPlayerCallbackHandler* callbacks = nullptr;
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = sb_audio_config,
+              .video_sample_info = sb_video_config,
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(DoAll(SaveArg<1>(&callbacks), Return(&sb_player_)));
+
+  EXPECT_CALL(starboard_, SeekTo(&sb_player_, kSeekTime.InMicroseconds(), _))
+      .Times(1);
+
+  // Set expectations for the video buffer.
+  scoped_refptr<::media::DecoderBuffer> video_buffer =
+      ::media::DecoderBuffer::CopyFrom(kVideoData);
+  video_buffer->set_timestamp(kVideoBufferTs);
+  const StarboardSampleInfo expected_video_info = {
+      .type = 1,
+      .buffer = video_buffer->data(),
+      .buffer_size = static_cast<int>(video_buffer->size()),
+      .timestamp = kVideoBufferTs.InMicroseconds(),
+      .side_data = base::span<const StarboardSampleSideData>(),
+      .video_sample_info = sb_video_config,
+      .drm_info = nullptr,
+  };
+  EXPECT_CALL(video_stream_, OnRead)
+      .WillOnce(RunOnceCallback<0>(
+          DemuxerStream::Status::kOk,
+          std::vector<scoped_refptr<::media::DecoderBuffer>>({video_buffer})));
+  EXPECT_CALL(
+      starboard_,
+      WriteSample(&sb_player_, StarboardMediaType::kStarboardMediaTypeVideo,
+                  ElementsAre(MatchesStarboardSampleInfo(expected_video_info))))
+      .Times(1);
+
+  // Set expectations for the audio buffer.
+  scoped_refptr<::media::DecoderBuffer> audio_buffer =
+      ::media::DecoderBuffer::CopyFrom(kAudioData);
+  audio_buffer->set_timestamp(kAudioBufferTs);
+  const StarboardSampleInfo expected_audio_info = {
+      .type = 0,
+      .buffer = audio_buffer->data(),
+      .buffer_size = static_cast<int>(audio_buffer->size()),
+      .timestamp = kAudioBufferTs.InMicroseconds(),
+      .side_data = base::span<const StarboardSampleSideData>(),
+      .audio_sample_info = sb_audio_config,
+      .drm_info = nullptr,
+  };
+  EXPECT_CALL(audio_stream_, OnRead)
+      .WillOnce(RunOnceCallback<0>(
+          DemuxerStream::Status::kOk,
+          std::vector<scoped_refptr<::media::DecoderBuffer>>({audio_buffer})));
+  EXPECT_CALL(
+      starboard_,
+      WriteSample(&sb_player_, StarboardMediaType::kStarboardMediaTypeAudio,
+                  ElementsAre(MatchesStarboardSampleInfo(expected_audio_info))))
+      .Times(1);
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  std::unique_ptr<StarboardPlayerManager> player_manager =
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, &video_stream_, &renderer_client_,
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+
+  player_manager->StartPlayingFrom(kSeekTime);
+
+  // Simulate Starboard requesting a video buffer, then an audio buffer. The
+  // player manager should read from the video stream and provide that buffer to
+  // starboard, then read from the audio stream and provide that buffer to
+  // starboard.
+  ASSERT_THAT(callbacks, NotNull());
+  ASSERT_THAT(callbacks->decoder_status_fn, NotNull());
+  ASSERT_THAT(callbacks->context, NotNull());
+  callbacks->decoder_status_fn(
+      &sb_player_, callbacks->context,
+      StarboardMediaType::kStarboardMediaTypeVideo,
+      StarboardDecoderState::kStarboardDecoderStateNeedsData, seek_ticket);
+
+  callbacks->decoder_status_fn(
+      &sb_player_, callbacks->context,
+      StarboardMediaType::kStarboardMediaTypeAudio,
+      StarboardDecoderState::kStarboardDecoderStateNeedsData, seek_ticket);
+}
+
+TEST_F(StarboardPlayerManagerTest,
+       VideoOnlyReadsFromDemuxerStreamAndWritesBufferToStarboard) {
+  constexpr auto kSeekTime = base::Seconds(10);
+  constexpr auto kVideoBufferTs = base::Milliseconds(10001);
+  constexpr auto kVideoData = std::to_array<uint8_t>({1, 2, 3, 4, 5});
+  const StarboardVideoSampleInfo sb_video_config = GetStarboardVideoConfig();
+
+  // This will be updated whenever the player manager seeks in starboard.
+  int seek_ticket = -1;
+  ON_CALL(starboard_, SeekTo(&sb_player_, _, _))
+      .WillByDefault(SaveArg<2>(&seek_ticket));
+
+  // This will be set to the callbacks received by the mock Starboard.
+  const StarboardPlayerCallbackHandler* callbacks = nullptr;
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = {},
+              .video_sample_info = sb_video_config,
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(DoAll(SaveArg<1>(&callbacks), Return(&sb_player_)));
+
+  EXPECT_CALL(starboard_, SeekTo(&sb_player_, kSeekTime.InMicroseconds(), _))
+      .Times(1);
+
+  // Set expectations for the video buffer.
+  scoped_refptr<::media::DecoderBuffer> video_buffer =
+      ::media::DecoderBuffer::CopyFrom(kVideoData);
+  video_buffer->set_timestamp(kVideoBufferTs);
+  const StarboardSampleInfo expected_video_info = {
+      .type = 1,
+      .buffer = video_buffer->data(),
+      .buffer_size = static_cast<int>(video_buffer->size()),
+      .timestamp = kVideoBufferTs.InMicroseconds(),
+      .side_data = base::span<const StarboardSampleSideData>(),
+      .video_sample_info = sb_video_config,
+      .drm_info = nullptr,
+  };
+  EXPECT_CALL(video_stream_, OnRead)
+      .WillOnce(RunOnceCallback<0>(
+          DemuxerStream::Status::kOk,
+          std::vector<scoped_refptr<::media::DecoderBuffer>>({video_buffer})));
+  EXPECT_CALL(
+      starboard_,
+      WriteSample(&sb_player_, StarboardMediaType::kStarboardMediaTypeVideo,
+                  ElementsAre(MatchesStarboardSampleInfo(expected_video_info))))
+      .Times(1);
+
+  video_stream_.set_video_decoder_config(GetChromiumVideoConfig());
+
+  std::unique_ptr<StarboardPlayerManager> player_manager =
+      StarboardPlayerManager::Create(
+          &starboard_, /*audio_stream=*/nullptr, &video_stream_,
+          &renderer_client_, base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+
+  player_manager->StartPlayingFrom(kSeekTime);
+
+  // Simulate Starboard requesting a video buffer. The player manager should
+  // read from the video stream and provide that buffer to starboard.
+  ASSERT_THAT(callbacks, NotNull());
+  ASSERT_THAT(callbacks->decoder_status_fn, NotNull());
+  ASSERT_THAT(callbacks->context, NotNull());
+  callbacks->decoder_status_fn(
+      &sb_player_, callbacks->context,
+      StarboardMediaType::kStarboardMediaTypeVideo,
+      StarboardDecoderState::kStarboardDecoderStateNeedsData, seek_ticket);
+}
+
+TEST_F(StarboardPlayerManagerTest,
+       AudioOnlyReadsFromDemuxerStreamAndWritesBufferToStarboard) {
+  constexpr auto kSeekTime = base::Seconds(10);
+  constexpr auto kAudioBufferTs = base::Milliseconds(10002);
+  constexpr auto kAudioData = std::to_array<uint8_t>({9, 8, 7});
+  const StarboardAudioSampleInfo sb_audio_config = GetStarboardAudioConfig();
+
+  // This will be updated whenever the player manager seeks in starboard.
+  int seek_ticket = -1;
+  ON_CALL(starboard_, SeekTo(&sb_player_, _, _))
+      .WillByDefault(SaveArg<2>(&seek_ticket));
+
+  // This will be set to the callbacks received by the mock Starboard.
+  const StarboardPlayerCallbackHandler* callbacks = nullptr;
+  EXPECT_CALL(
+      starboard_,
+      CreatePlayer(
+          Pointee(MatchesPlayerCreationParam(StarboardPlayerCreationParam{
+              .drm_system = &drm_system_,
+              .audio_sample_info = sb_audio_config,
+              .video_sample_info = {},
+              .output_mode = StarboardPlayerOutputMode::
+                  kStarboardPlayerOutputModePunchOut})),
+          _))
+      .WillOnce(DoAll(SaveArg<1>(&callbacks), Return(&sb_player_)));
+
+  EXPECT_CALL(starboard_, SeekTo(&sb_player_, kSeekTime.InMicroseconds(), _))
+      .Times(1);
+
+  // Set expectations for the audio buffer.
+  scoped_refptr<::media::DecoderBuffer> audio_buffer =
+      ::media::DecoderBuffer::CopyFrom(kAudioData);
+  audio_buffer->set_timestamp(kAudioBufferTs);
+  const StarboardSampleInfo expected_audio_info = {
+      .type = 0,
+      .buffer = audio_buffer->data(),
+      .buffer_size = static_cast<int>(audio_buffer->size()),
+      .timestamp = kAudioBufferTs.InMicroseconds(),
+      .side_data = base::span<const StarboardSampleSideData>(),
+      .audio_sample_info = sb_audio_config,
+      .drm_info = nullptr,
+  };
+  EXPECT_CALL(audio_stream_, OnRead)
+      .WillOnce(RunOnceCallback<0>(
+          DemuxerStream::Status::kOk,
+          std::vector<scoped_refptr<::media::DecoderBuffer>>({audio_buffer})));
+  EXPECT_CALL(
+      starboard_,
+      WriteSample(&sb_player_, StarboardMediaType::kStarboardMediaTypeAudio,
+                  ElementsAre(MatchesStarboardSampleInfo(expected_audio_info))))
+      .Times(1);
+
+  audio_stream_.set_audio_decoder_config(GetChromiumAudioConfig());
+
+  std::unique_ptr<StarboardPlayerManager> player_manager =
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, /*video_stream=*/nullptr,
+          &renderer_client_, base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true);
+  ASSERT_THAT(player_manager, NotNull());
+
+  player_manager->StartPlayingFrom(kSeekTime);
+
+  // Simulate Starboard requesting a video buffer, then an audio buffer. The
+  // player manager should read from the video stream and provide that buffer to
+  // starboard, then read from the audio stream and provide that buffer to
+  // starboard.
+  ASSERT_THAT(callbacks, NotNull());
+  ASSERT_THAT(callbacks->decoder_status_fn, NotNull());
+  ASSERT_THAT(callbacks->context, NotNull());
+
+  callbacks->decoder_status_fn(
+      &sb_player_, callbacks->context,
+      StarboardMediaType::kStarboardMediaTypeAudio,
+      StarboardDecoderState::kStarboardDecoderStateNeedsData, seek_ticket);
+}
+
+TEST_F(StarboardPlayerManagerTest,
+       CreatePlayerReturnsNullIfBothDemuxerStreamsAreNull) {
+  EXPECT_THAT(
+      StarboardPlayerManager::Create(
+          &starboard_, /*audio_stream=*/nullptr, /*video_stream=*/nullptr,
+          &renderer_client_, base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true),
+      IsNull());
+}
+
+TEST_F(StarboardPlayerManagerTest, CreatePlayerReturnsNullIfStarboardIsNull) {
+  EXPECT_THAT(
+      StarboardPlayerManager::Create(
+          /*starboard=*/nullptr, &audio_stream_, &video_stream_,
+          &renderer_client_, base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true),
+      IsNull());
+}
+
+TEST_F(StarboardPlayerManagerTest,
+       CreatePlayerReturnsNullIfRendererClientIsNull) {
+  EXPECT_THAT(
+      StarboardPlayerManager::Create(
+          &starboard_, &audio_stream_, &video_stream_,
+          /*client=*/nullptr, base::SequencedTaskRunner::GetCurrentDefault(),
+          /*enable_buffering=*/true),
+      IsNull());
+}
+
+TEST_F(StarboardPlayerManagerTest, CreatePlayerReturnsNullIfTaskRunnerIsNull) {
+  EXPECT_THAT(StarboardPlayerManager::Create(&starboard_, &audio_stream_,
+                                             &video_stream_, &renderer_client_,
+                                             /*media_task_runner=*/nullptr,
+                                             /*enable_buffering=*/true),
+              IsNull());
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 1480393..82979b63 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16309.0.0-1069370
\ No newline at end of file
+16310.0.0-1069388
\ No newline at end of file
diff --git a/chromeos/ash/components/audio/audio_device_id.cc b/chromeos/ash/components/audio/audio_device_id.cc
index 3470f63c..67b178ac 100644
--- a/chromeos/ash/components/audio/audio_device_id.cc
+++ b/chromeos/ash/components/audio/audio_device_id.cc
@@ -4,6 +4,8 @@
 
 #include "chromeos/ash/components/audio/audio_device_id.h"
 
+#include <sstream>
+
 #include "base/check.h"
 #include "base/check_op.h"
 #include "base/containers/flat_set.h"
diff --git a/chromeos/ash/components/audio/audio_device_id.h b/chromeos/ash/components/audio/audio_device_id.h
index e845fef..7d788577f 100644
--- a/chromeos/ash/components/audio/audio_device_id.h
+++ b/chromeos/ash/components/audio/audio_device_id.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_ASH_COMPONENTS_AUDIO_AUDIO_DEVICE_ID_H_
 #define CHROMEOS_ASH_COMPONENTS_AUDIO_AUDIO_DEVICE_ID_H_
 
+#include <optional>
+
 #include "chromeos/ash/components/audio/audio_device.h"
 
 namespace ash {
diff --git a/chromeos/ash/components/file_manager/indexing/file_index.cc b/chromeos/ash/components/file_manager/indexing/file_index.cc
index 1300aa2..84d089d 100644
--- a/chromeos/ash/components/file_manager/indexing/file_index.cc
+++ b/chromeos/ash/components/file_manager/indexing/file_index.cc
@@ -4,6 +4,12 @@
 
 #include "chromeos/ash/components/file_manager/indexing/file_index.h"
 
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include <utility>
+#include <vector>
+
 #include "base/time/time.h"
 
 namespace ash::file_manager {
@@ -144,19 +150,19 @@
     if (term_id == -1) {
       return results;
     }
-    const std::set<int64_t> url_ids = storage_->GetUrlIdsForTermId(term_id);
+    std::set<int64_t> url_ids = storage_->GetUrlIdsForTermId(term_id);
     if (url_ids.empty()) {
       return results;
     }
     if (first) {
-      matched_url_ids = url_ids;
+      matched_url_ids = std::move(url_ids);
       first = false;
     } else {
       std::set<int64_t> intersection;
-      std::set_intersection(matched_url_ids.begin(), matched_url_ids.end(),
-                            url_ids.begin(), url_ids.end(),
-                            std::inserter(intersection, intersection.begin()));
-      matched_url_ids = intersection;
+      std::ranges::set_intersection(
+          matched_url_ids, url_ids,
+          std::inserter(intersection, intersection.begin()));
+      matched_url_ids = std::move(intersection);
     }
     if (matched_url_ids.empty()) {
       break;
diff --git a/chromeos/ash/components/kcer/kcer_nss/kcer_nss_unittest.cc b/chromeos/ash/components/kcer/kcer_nss/kcer_nss_unittest.cc
index 602cf88b..77c8e90 100644
--- a/chromeos/ash/components/kcer/kcer_nss/kcer_nss_unittest.cc
+++ b/chromeos/ash/components/kcer/kcer_nss/kcer_nss_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/base64.h"
 #include "base/files/file_util.h"
 #include "base/memory/raw_ref.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/task/bind_post_task.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/gmock_move_support.h"
diff --git a/chromeos/ash/components/local_search_service/shared_structs.h b/chromeos/ash/components/local_search_service/shared_structs.h
index 1a875ef..f59422d 100644
--- a/chromeos/ash/components/local_search_service/shared_structs.h
+++ b/chromeos/ash/components/local_search_service/shared_structs.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_ASH_COMPONENTS_LOCAL_SEARCH_SERVICE_SHARED_STRUCTS_H_
 #define CHROMEOS_ASH_COMPONENTS_LOCAL_SEARCH_SERVICE_SHARED_STRUCTS_H_
 
+#include <stdint.h>
+
 #include <string>
 #include <vector>
 
diff --git a/chromeos/ash/components/login/auth/recovery/service_constants.h b/chromeos/ash/components/login/auth/recovery/service_constants.h
index ab7af4b3..29231e7 100644
--- a/chromeos/ash/components/login/auth/recovery/service_constants.h
+++ b/chromeos/ash/components/login/auth/recovery/service_constants.h
@@ -5,8 +5,11 @@
 #ifndef CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_RECOVERY_SERVICE_CONSTANTS_H_
 #define CHROMEOS_ASH_COMPONENTS_LOGIN_AUTH_RECOVERY_SERVICE_CONSTANTS_H_
 
+#include <stdint.h>
+
 #include <string>
 #include <vector>
+
 #include "base/component_export.h"
 
 class GURL;
diff --git a/chromeos/ash/components/nearby/presence/credentials/local_device_data_provider.h b/chromeos/ash/components/nearby/presence/credentials/local_device_data_provider.h
index 6d76409..ac7579d 100644
--- a/chromeos/ash/components/nearby/presence/credentials/local_device_data_provider.h
+++ b/chromeos/ash/components/nearby/presence/credentials/local_device_data_provider.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 namespace nearby::internal {
 class SharedCredential;
diff --git a/chromeos/ash/components/wifi_p2p/wifi_p2p_group.h b/chromeos/ash/components/wifi_p2p/wifi_p2p_group.h
index 5b00cda9..e5a5f980c 100644
--- a/chromeos/ash/components/wifi_p2p/wifi_p2p_group.h
+++ b/chromeos/ash/components/wifi_p2p/wifi_p2p_group.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_ASH_COMPONENTS_WIFI_P2P_WIFI_P2P_GROUP_H_
 #define CHROMEOS_ASH_COMPONENTS_WIFI_P2P_WIFI_P2P_GROUP_H_
 
+#include <stdint.h>
+
 #include <string>
 
 #include "base/component_export.h"
diff --git a/chromeos/ash/services/connectivity/public/cpp/fake_passpoint_subscription.h b/chromeos/ash/services/connectivity/public/cpp/fake_passpoint_subscription.h
index 804db90..e0fcc039 100644
--- a/chromeos/ash/services/connectivity/public/cpp/fake_passpoint_subscription.h
+++ b/chromeos/ash/services/connectivity/public/cpp/fake_passpoint_subscription.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_ASH_SERVICES_CONNECTIVITY_PUBLIC_CPP_FAKE_PASSPOINT_SUBSCRIPTION_H_
 #define CHROMEOS_ASH_SERVICES_CONNECTIVITY_PUBLIC_CPP_FAKE_PASSPOINT_SUBSCRIPTION_H_
 
+#include <stdint.h>
+
 #include <optional>
 #include <string>
 #include <vector>
diff --git a/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.h b/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.h
index e59eaf7c..be8051dd 100644
--- a/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.h
+++ b/chromeos/ash/services/quick_pair/public/cpp/account_key_filter.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_ASH_SERVICES_QUICK_PAIR_PUBLIC_CPP_ACCOUNT_KEY_FILTER_H_
 #define CHROMEOS_ASH_SERVICES_QUICK_PAIR_PUBLIC_CPP_ACCOUNT_KEY_FILTER_H_
 
+#include <stdint.h>
+
 #include <vector>
 
 namespace ash {
diff --git a/chromeos/ash/services/secure_channel/ble_advertisement_generator.h b/chromeos/ash/services/secure_channel/ble_advertisement_generator.h
index 784df63..0ab2a0c 100644
--- a/chromeos/ash/services/secure_channel/ble_advertisement_generator.h
+++ b/chromeos/ash/services/secure_channel/ble_advertisement_generator.h
@@ -6,6 +6,7 @@
 #define CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_BLE_ADVERTISEMENT_GENERATOR_H_
 
 #include <memory>
+#include <string>
 
 namespace ash {
 
diff --git a/chromeos/ash/services/secure_channel/data_with_timestamp.h b/chromeos/ash/services/secure_channel/data_with_timestamp.h
index b371d5c..b0d5695 100644
--- a/chromeos/ash/services/secure_channel/data_with_timestamp.h
+++ b/chromeos/ash/services/secure_channel/data_with_timestamp.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_DATA_WITH_TIMESTAMP_H_
 #define CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_DATA_WITH_TIMESTAMP_H_
 
+#include <stdint.h>
+
 #include <string>
 #include <vector>
 
diff --git a/chromeos/ash/services/secure_channel/raw_eid_generator.h b/chromeos/ash/services/secure_channel/raw_eid_generator.h
index 093cc64..5a950ab 100644
--- a/chromeos/ash/services/secure_channel/raw_eid_generator.h
+++ b/chromeos/ash/services/secure_channel/raw_eid_generator.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_RAW_EID_GENERATOR_H_
 #define CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_RAW_EID_GENERATOR_H_
 
+#include <stdint.h>
+
 #include <string>
 
 namespace ash::secure_channel {
diff --git a/chromeos/utils/pdf_conversion.h b/chromeos/utils/pdf_conversion.h
index fc134bc9..6c24f7c 100644
--- a/chromeos/utils/pdf_conversion.h
+++ b/chromeos/utils/pdf_conversion.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_UTILS_PDF_CONVERSION_H_
 #define CHROMEOS_UTILS_PDF_CONVERSION_H_
 
+#include <stdint.h>
+
 #include <optional>
 #include <string>
 #include <vector>
diff --git a/clank b/clank
index 9d5f4e3e..4d4ffc26 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 9d5f4e3ec164804c21a8a7c92eed98c1e0094c13
+Subproject commit 4d4ffc26471ca1d5eb0d122765d87b359956f635
diff --git a/components/autofill/core/browser/data_manager/valuables/valuables_data_manager.cc b/components/autofill/core/browser/data_manager/valuables/valuables_data_manager.cc
index 5f2d3e9..6c086bc 100644
--- a/components/autofill/core/browser/data_manager/valuables/valuables_data_manager.cc
+++ b/components/autofill/core/browser/data_manager/valuables/valuables_data_manager.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "base/metrics/histogram_macros.h"
 #include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
 #include "components/autofill/core/browser/ui/autofill_image_fetcher_base.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
@@ -95,6 +96,10 @@
   // for caching loyalty card icons.
   ProcessLoyaltyCardIconUrlChanges();
   NotifyObservers();
+
+  // Log the overall counts.
+  UMA_HISTOGRAM_COUNTS_1000("Autofill.LoyaltyCard.StoredCardsCount",
+                            loyalty_cards_.size());
 }
 
 void ValuablesDataManager::ProcessLoyaltyCardIconUrlChanges() {
diff --git a/components/autofill/core/browser/data_manager/valuables/valuables_data_manager_unittest.cc b/components/autofill/core/browser/data_manager/valuables/valuables_data_manager_unittest.cc
index 5dd20ca..8a44664 100644
--- a/components/autofill/core/browser/data_manager/valuables/valuables_data_manager_unittest.cc
+++ b/components/autofill/core/browser/data_manager/valuables/valuables_data_manager_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/functional/callback_helpers.h"
 #include "base/scoped_observation.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "components/autofill/core/browser/data_model/valuables/loyalty_card.h"
@@ -95,6 +96,7 @@
 // Tests that the `ValuablesDataManager` correctly generates loyalty cards to
 // suggest ordered by merchant name.
 TEST_F(ValuablesDataManagerTest, GetLoyaltyCardsToSuggest) {
+  base::HistogramTester histogram_tester;
   const LoyaltyCard card1 = LoyaltyCard(
       /*loyalty_card_id=*/ValuableId("loyalty_card_id_1"),
       /*merchant_name=*/"CVS Pharmacy",
@@ -126,11 +128,16 @@
   helper().WaitUntilIdle();
   EXPECT_THAT(valuables_data_manager.GetLoyaltyCardsToSuggest(),
               ElementsAre(card1, card3, card2));
+  // Validate the basic count metrics.
+  histogram_tester.ExpectTotalCount("Autofill.LoyaltyCard.StoredCardsCount", 1);
+  histogram_tester.ExpectBucketCount("Autofill.LoyaltyCard.StoredCardsCount", 3,
+                                     1);
 }
 
 // Verify that the `ValuablesDataManager` correctly updates the list of loyalty
 // cards when the Chrome Sync writes them to the database.
 TEST_F(ValuablesDataManagerTest, DataChangedBySync) {
+  base::HistogramTester histogram_tester;
   const LoyaltyCard card1 = test::CreateLoyaltyCard();
   const LoyaltyCard card2 = test::CreateLoyaltyCard2();
   {
@@ -179,6 +186,13 @@
   helper().WaitUntilIdle();
   EXPECT_THAT(valuables_data_manager.GetLoyaltyCards(),
               UnorderedElementsAre(card1, card2));
+
+  // Validate the basic count metrics.
+  histogram_tester.ExpectTotalCount("Autofill.LoyaltyCard.StoredCardsCount", 2);
+  histogram_tester.ExpectBucketCount("Autofill.LoyaltyCard.StoredCardsCount", 1,
+                                     1);
+  histogram_tester.ExpectBucketCount("Autofill.LoyaltyCard.StoredCardsCount", 2,
+                                     1);
 }
 
 TEST_F(ValuablesDataManagerTest, GetCachedValuableImageForUrl) {
diff --git a/components/autofill/core/browser/filling/field_filling_util_unittest.cc b/components/autofill/core/browser/filling/field_filling_util_unittest.cc
index f32b2de..ab31314 100644
--- a/components/autofill/core/browser/filling/field_filling_util_unittest.cc
+++ b/components/autofill/core/browser/filling/field_filling_util_unittest.cc
@@ -7,6 +7,7 @@
 #include <optional>
 #include <string>
 
+#include "base/strings/string_number_conversions.h"
 #include "components/autofill/core/browser/autofill_field.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/autofill/core/browser/payments/payments_network_interface_unittest.cc b/components/autofill/core/browser/payments/payments_network_interface_unittest.cc
index 781ffb3f..da50c0bf 100644
--- a/components/autofill/core/browser/payments/payments_network_interface_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_network_interface_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/strings/escape.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
diff --git a/components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager_unittest.cc b/components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager_unittest.cc
index 72c45c37..253e113 100644
--- a/components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager_unittest.cc
+++ b/components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager_unittest.cc
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/functional/callback_helpers.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
diff --git a/components/autofill/core/browser/suggestions/payments/iban_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/payments/iban_suggestion_generator_unittest.cc
index ffad19f5..d7c41bb 100644
--- a/components/autofill/core/browser/suggestions/payments/iban_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/payments/iban_suggestion_generator_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.h"
 
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_otp_input_dialog_controller_impl.cc b/components/autofill/core/browser/ui/payments/card_unmask_otp_input_dialog_controller_impl.cc
index b680c9c6..a4bcfb9a 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_otp_input_dialog_controller_impl.cc
+++ b/components/autofill/core/browser/ui/payments/card_unmask_otp_input_dialog_controller_impl.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.h"
 #include "components/autofill/core/browser/payments/otp_unmask_delegate.h"
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index 8f3b809..f07cb97 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -1255,6 +1255,9 @@
     <message name="IDS_AUTOFILL_LOYALTY_CARD_AUTOFILL_BUTTON" desc="The label of the button, which would fill in the web form with a loyalty card number." formatter_data="android_java">
       Autofill
     </message>
+    <message name="IDS_AUTOFILL_LOYALTY_CARD_WALLET_SETTINGS_BUTTON" desc="The label of the button, which redirects the user to the Google Wallet loyalty card settings in Chrome." formatter_data="android_java">
+      Wallet settings
+    </message>
     <message name="IDS_AUTOFILL_PAYMENT_METHOD_BOTTOM_SHEET_CONTENT_DESCRIPTION" desc="Accessibility string read when the bottom sheet is opened. It describes the bottom sheet where a user can pick a payment method to fill into a form." formatter_data="android_java">
       Payment methods available to be filled on touch. Keyboard hidden.
     </message>
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_LOYALTY_CARD_WALLET_SETTINGS_BUTTON.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_LOYALTY_CARD_WALLET_SETTINGS_BUTTON.png.sha1
new file mode 100644
index 0000000..cdf55eb
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_LOYALTY_CARD_WALLET_SETTINGS_BUTTON.png.sha1
@@ -0,0 +1 @@
+9e24b99eda473bd6ebb658fed011e22ef776556a
\ No newline at end of file
diff --git a/components/bookmarks/browser/bookmark_utils_unittest.cc b/components/bookmarks/browser/bookmark_utils_unittest.cc
index e33a7be..98c5848 100644
--- a/components/bookmarks/browser/bookmark_utils_unittest.cc
+++ b/components/bookmarks/browser/bookmark_utils_unittest.cc
@@ -13,6 +13,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
diff --git a/components/bookmarks/browser/model_loader_unittest.cc b/components/bookmarks/browser/model_loader_unittest.cc
index 21e9a58..d1bf06d8 100644
--- a/components/bookmarks/browser/model_loader_unittest.cc
+++ b/components/bookmarks/browser/model_loader_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/no_destructor.h"
 #include "base/path_service.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
diff --git a/components/browser_sync/sync_to_signin_migration_unittest.cc b/components/browser_sync/sync_to_signin_migration_unittest.cc
index 4fc9f7d..40380c8 100644
--- a/components/browser_sync/sync_to_signin_migration_unittest.cc
+++ b/components/browser_sync/sync_to_signin_migration_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "base/threading/thread_restrictions.h"
 #include "components/browser_sync/browser_sync_switches.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/signin/public/base/signin_pref_names.h"
diff --git a/components/capture_mode/DEPS b/components/capture_mode/DEPS
index 2e1ccac..28091f9 100644
--- a/components/capture_mode/DEPS
+++ b/components/capture_mode/DEPS
@@ -8,7 +8,6 @@
   "+gpu/command_buffer/common/shared_image_usage.h",
   "+gpu/config/gpu_finch_features.h",
   "+gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h",
-  "+gpu/ipc/common/gpu_memory_buffer_support.h",
   "+media",
   "+mojo/public",
   "+services/audio/public/cpp/device_factory.h",
diff --git a/components/collaboration/internal/collaboration_controller.cc b/components/collaboration/internal/collaboration_controller.cc
index 33d20c6..962f443 100644
--- a/components/collaboration/internal/collaboration_controller.cc
+++ b/components/collaboration/internal/collaboration_controller.cc
@@ -27,6 +27,7 @@
 
 namespace collaboration {
 
+using metrics::CollaborationServiceFlowEvent;
 using metrics::CollaborationServiceJoinEvent;
 using metrics::CollaborationServiceShareOrManageEvent;
 
@@ -282,6 +283,9 @@
           GetLogger(), controller_->flow().type,
           CollaborationServiceJoinEvent::kAccountInfoNotReadyOnSignin,
           CollaborationServiceShareOrManageEvent::kAccountInfoNotReadyOnSignin);
+      RecordCollaborationFlowEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceFlowEvent::kAccountInfoNotReadyOnSignin);
       pending_status_change_observer_.Observe(
           controller_->collaboration_service());
       return;
@@ -299,6 +303,9 @@
           GetLogger(), controller_->flow().type,
           CollaborationServiceJoinEvent::kDevicePolicyDisableSignin,
           CollaborationServiceShareOrManageEvent::kDevicePolicyDisableSignin);
+      RecordCollaborationFlowEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceFlowEvent::kDevicePolicyDisableSignin);
       HandleErrorWithType(ErrorInfo::Type::kSigninDisabledByPolicy);
       return;
     }
@@ -307,6 +314,9 @@
         GetLogger(), controller_->flow().type,
         CollaborationServiceJoinEvent::kManagedAccountSignin,
         CollaborationServiceShareOrManageEvent::kManagedAccountSignin);
+    RecordCollaborationFlowEvent(
+        GetLogger(), controller_->flow().type,
+        CollaborationServiceFlowEvent::kManagedAccountSignin);
     HandleErrorWithType(ErrorInfo::Type::kSyncDisabledByPolicy);
   }
 
@@ -355,18 +365,12 @@
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     start_time_ = base::Time::Now();
     FlowType flow_type = controller_->flow().type;
-    switch (flow_type) {
-      case FlowType::kJoin:
-        RecordJoinEvent(GetLogger(),
-                        CollaborationServiceJoinEvent::kNotSignedIn);
-        break;
-      case FlowType::kShareOrManage:
-        RecordShareOrManageEvent(
-            GetLogger(), CollaborationServiceShareOrManageEvent::kNotSignedIn);
-        break;
-      case FlowType::kLeaveOrDelete:
-        break;
-    }
+    RecordJoinOrShareOrManageEvent(
+        GetLogger(), controller_->flow().type,
+        CollaborationServiceJoinEvent::kNotSignedIn,
+        CollaborationServiceShareOrManageEvent::kNotSignedIn);
+    RecordCollaborationFlowEvent(GetLogger(), flow_type,
+                                 CollaborationServiceFlowEvent::kNotSignedIn);
 
     controller_->delegate()->ShowAuthenticationUi(
         flow_type, base::BindOnce(&AuthenticatingState::ProcessOutcome,
@@ -376,14 +380,13 @@
   void ProcessOutcome(Outcome outcome) override {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     if (Outcome::kCancel == outcome) {
-      if (FlowType::kJoin == controller_->flow().type) {
-        RecordJoinEvent(GetLogger(),
-                        CollaborationServiceJoinEvent::kCanceledNotSignedIn);
-      } else if (FlowType::kShareOrManage == controller_->flow().type) {
-        RecordShareOrManageEvent(
-            GetLogger(),
-            CollaborationServiceShareOrManageEvent::kCanceledNotSignedIn);
-      }
+      RecordJoinOrShareOrManageEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceJoinEvent::kCanceledNotSignedIn,
+          CollaborationServiceShareOrManageEvent::kCanceledNotSignedIn);
+      RecordCollaborationFlowEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceFlowEvent::kCanceledNotSignedIn);
     }
 
     ControllerState::ProcessOutcome(outcome);
@@ -398,6 +401,7 @@
       return;
     }
 
+    FlowType flow_type = controller_->flow().type;
     if (!status.IsAuthenticationValid()) {
       // Set up the timeout exit task.
       base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
@@ -407,25 +411,22 @@
           base::Minutes(30));
       collaboration_service_observer_.Observe(
           controller_->collaboration_service());
-      if (FlowType::kJoin == controller_->flow().type) {
-        RecordJoinEvent(
-            GetLogger(),
-            CollaborationServiceJoinEvent::kSigninVerificationFailed);
-      } else if (FlowType::kShareOrManage == controller_->flow().type) {
-        RecordShareOrManageEvent(
-            GetLogger(),
-            CollaborationServiceShareOrManageEvent::kSigninVerificationFailed);
-      }
+      RecordJoinOrShareOrManageEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceJoinEvent::kSigninVerificationFailed,
+          CollaborationServiceShareOrManageEvent::kSigninVerificationFailed);
+      RecordCollaborationFlowEvent(
+          GetLogger(), flow_type,
+          CollaborationServiceFlowEvent::kSigninVerificationFailed);
       return;
     }
 
-    if (FlowType::kJoin == controller_->flow().type) {
-      RecordJoinEvent(GetLogger(),
-                      CollaborationServiceJoinEvent::kSigninVerified);
-    } else if (FlowType::kShareOrManage == controller_->flow().type) {
-      RecordShareOrManageEvent(
-          GetLogger(), CollaborationServiceShareOrManageEvent::kSigninVerified);
-    }
+    RecordJoinOrShareOrManageEvent(
+        GetLogger(), controller_->flow().type,
+        CollaborationServiceJoinEvent::kSigninVerified,
+        CollaborationServiceShareOrManageEvent::kSigninVerified);
+    RecordCollaborationFlowEvent(
+        GetLogger(), flow_type, CollaborationServiceFlowEvent::kSigninVerified);
     // TODO(crbug.com/380957996): Handle signin/sync changes during a flow.
     FinishAndTransition();
   }
@@ -440,15 +441,13 @@
     }
 
     if (status.IsAuthenticationValid()) {
-      if (FlowType::kJoin == controller_->flow().type) {
-        RecordJoinEvent(
-            GetLogger(),
-            CollaborationServiceJoinEvent::kSigninVerifiedInObserver);
-      } else if (FlowType::kShareOrManage == controller_->flow().type) {
-        RecordShareOrManageEvent(
-            GetLogger(),
-            CollaborationServiceShareOrManageEvent::kSigninVerifiedInObserver);
-      }
+      RecordJoinOrShareOrManageEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceJoinEvent::kSigninVerifiedInObserver,
+          CollaborationServiceShareOrManageEvent::kSigninVerifiedInObserver);
+      RecordCollaborationFlowEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceFlowEvent::kSigninVerifiedInObserver);
       FinishAndTransition();
     }
   }
@@ -498,15 +497,13 @@
     if (!is_data_sharing_ready_) {
       data_sharing_observer_.Observe(controller_->data_sharing_service());
     } else {
-      if (FlowType::kJoin == controller_->flow().type) {
-        RecordJoinEvent(
-            GetLogger(),
-            CollaborationServiceJoinEvent::kDataSharingReadyWhenStarted);
-      } else if (FlowType::kShareOrManage == controller_->flow().type) {
-        RecordShareOrManageEvent(GetLogger(),
-                                 CollaborationServiceShareOrManageEvent::
-                                     kDataSharingReadyWhenStarted);
-      }
+      RecordJoinOrShareOrManageEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceJoinEvent::kDataSharingReadyWhenStarted,
+          CollaborationServiceShareOrManageEvent::kDataSharingReadyWhenStarted);
+      RecordCollaborationFlowEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceFlowEvent::kDataSharingReadyWhenStarted);
     }
     tab_group_sync_observer_.Observe(controller_->tab_group_sync_service());
   }
@@ -523,14 +520,13 @@
   // TabGroupSyncService::Observer implementation.
   void OnInitialized() override {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    if (FlowType::kJoin == controller_->flow().type) {
-      RecordJoinEvent(GetLogger(),
-                      CollaborationServiceJoinEvent::kTabGroupServiceReady);
-    } else if (FlowType::kShareOrManage == controller_->flow().type) {
-      RecordShareOrManageEvent(
-          GetLogger(),
-          CollaborationServiceShareOrManageEvent::kTabGroupServiceReady);
-    }
+    RecordJoinOrShareOrManageEvent(
+        GetLogger(), controller_->flow().type,
+        CollaborationServiceJoinEvent::kTabGroupServiceReady,
+        CollaborationServiceShareOrManageEvent::kTabGroupServiceReady);
+    RecordCollaborationFlowEvent(
+        GetLogger(), controller_->flow().type,
+        CollaborationServiceFlowEvent::kTabGroupServiceReady);
     is_tab_group_sync_ready_ = true;
     MaybeProceed();
   }
@@ -538,15 +534,14 @@
   // DataSharingService::Observer implementation.
   void OnGroupDataModelLoaded() override {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    if (FlowType::kJoin == controller_->flow().type) {
-      RecordJoinEvent(
-          GetLogger(),
-          CollaborationServiceJoinEvent::kDataSharingServiceReadyObserved);
-    } else if (FlowType::kShareOrManage == controller_->flow().type) {
-      RecordShareOrManageEvent(GetLogger(),
-                               CollaborationServiceShareOrManageEvent::
-                                   kDataSharingServiceReadyObserved);
-    }
+    RecordJoinOrShareOrManageEvent(
+        GetLogger(), controller_->flow().type,
+        CollaborationServiceJoinEvent::kDataSharingServiceReadyObserved,
+        CollaborationServiceShareOrManageEvent::
+            kDataSharingServiceReadyObserved);
+    RecordCollaborationFlowEvent(
+        GetLogger(), controller_->flow().type,
+        CollaborationServiceFlowEvent::kDataSharingServiceReadyObserved);
 
     is_data_sharing_ready_ = true;
     MaybeProceed();
@@ -556,15 +551,13 @@
   void MaybeProceed() {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     if (is_tab_group_sync_ready_ && is_data_sharing_ready_) {
-      if (FlowType::kJoin == controller_->flow().type) {
-        RecordJoinEvent(
-            GetLogger(),
-            CollaborationServiceJoinEvent::kAllServicesReadyForFlow);
-      } else if (FlowType::kShareOrManage == controller_->flow().type) {
-        RecordShareOrManageEvent(
-            GetLogger(),
-            CollaborationServiceShareOrManageEvent::kAllServicesReadyForFlow);
-      }
+      RecordJoinOrShareOrManageEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceJoinEvent::kAllServicesReadyForFlow,
+          CollaborationServiceShareOrManageEvent::kAllServicesReadyForFlow);
+      RecordCollaborationFlowEvent(
+          GetLogger(), controller_->flow().type,
+          CollaborationServiceFlowEvent::kAllServicesReadyForFlow);
       OnProcessingFinishedWithSuccess();
     }
   }
@@ -587,6 +580,13 @@
 
   void OnEnter(const ErrorInfo& error) override {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+    RecordJoinOrShareOrManageEvent(
+        GetLogger(), controller_->flow().type,
+        CollaborationServiceJoinEvent::kFlowRequirementsMet,
+        CollaborationServiceShareOrManageEvent::kFlowRequirementsMet);
+    RecordCollaborationFlowEvent(
+        GetLogger(), controller_->flow().type,
+        CollaborationServiceFlowEvent::kFlowRequirementsMet);
     switch (controller_->flow().type) {
       case FlowType::kJoin:
         CheckJoinFlowRequirements();
@@ -603,9 +603,6 @@
  private:
   void CheckJoinFlowRequirements() {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    RecordJoinEvent(GetLogger(),
-                    CollaborationServiceJoinEvent::kFlowRequirementsMet);
-
     const data_sharing::GroupId group_id =
         controller_->flow().join_token().group_id;
     // Check if user is already part of the group.
@@ -628,10 +625,6 @@
 
   void CheckShareFlowRequirements() {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    RecordShareOrManageEvent(
-        GetLogger(),
-        CollaborationServiceShareOrManageEvent::kFlowRequirementsMet);
-
     std::optional<tab_groups::SavedTabGroup> sync_group =
         controller_->tab_group_sync_service()->GetGroup(
             controller_->flow().either_id());
@@ -1380,6 +1373,12 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   tab_group_sync_service_observer_.Observe(tab_group_sync_service_);
 
+  RecordJoinOrShareOrManageEvent(
+      data_sharing_service_->GetLogger(), flow_.type,
+      CollaborationServiceJoinEvent::kStarted,
+      CollaborationServiceShareOrManageEvent::kStarted);
+  RecordCollaborationFlowEvent(data_sharing_service_->GetLogger(), flow_.type,
+                               CollaborationServiceFlowEvent::kStarted);
   current_state_ = std::make_unique<PendingState>(
       StateId::kPending, this,
       base::BindOnce(&CollaborationController::Exit,
diff --git a/components/collaboration/internal/collaboration_controller_unittest.cc b/components/collaboration/internal/collaboration_controller_unittest.cc
index 848081c1..1a5903dc 100644
--- a/components/collaboration/internal/collaboration_controller_unittest.cc
+++ b/components/collaboration/internal/collaboration_controller_unittest.cc
@@ -266,8 +266,8 @@
   run_loop.Run();
 
   histogram_tester.ExpectBucketCount(
-      "CollaborationService.JoinFlow",
-      metrics::CollaborationServiceJoinEvent::kFlowRequirementsMet, 1);
+      "CollaborationService.JoinFlow.Events",
+      metrics::CollaborationServiceFlowEvent::kFlowRequirementsMet, 1);
   histogram_tester.ExpectBucketCount(
       "CollaborationService.JoinFlow",
       metrics::CollaborationServiceJoinEvent::kAccepted, 1);
@@ -580,14 +580,14 @@
 
   // Verify the not signed in metrics are recorded properly.
   histogram_tester.ExpectBucketCount(
-      "CollaborationService.JoinFlow",
-      metrics::CollaborationServiceJoinEvent::kNotSignedIn, 1);
+      "CollaborationService.JoinFlow.Events",
+      metrics::CollaborationServiceFlowEvent::kNotSignedIn, 1);
   histogram_tester.ExpectBucketCount(
-      "CollaborationService.JoinFlow",
-      metrics::CollaborationServiceJoinEvent::kCanceledNotSignedIn, 1);
+      "CollaborationService.JoinFlow.Events",
+      metrics::CollaborationServiceFlowEvent::kCanceledNotSignedIn, 1);
   histogram_tester.ExpectBucketCount(
-      "CollaborationService.JoinFlow",
-      metrics::CollaborationServiceJoinEvent::kFlowRequirementsMet, 0);
+      "CollaborationService.JoinFlow.Events",
+      metrics::CollaborationServiceFlowEvent::kFlowRequirementsMet, 0);
 }
 
 TEST_F(CollaborationControllerTest, AuthenticationCanceledAfterSignIn) {
@@ -801,8 +801,8 @@
 
   // Verify the manage flow metrics are recorded properly.
   histogram_tester.ExpectBucketCount(
-      "CollaborationService.ShareOrManageFlow",
-      metrics::CollaborationServiceShareOrManageEvent::kFlowRequirementsMet, 1);
+      "CollaborationService.ShareOrManageFlow.Events",
+      metrics::CollaborationServiceFlowEvent::kFlowRequirementsMet, 1);
   histogram_tester.ExpectBucketCount(
       "CollaborationService.ShareOrManageFlow",
       metrics::CollaborationServiceShareOrManageEvent::kShareDialogShown, 1);
@@ -852,8 +852,8 @@
 
   // Verify the manage flow metrics are recorded properly.
   histogram_tester.ExpectBucketCount(
-      "CollaborationService.ShareOrManageFlow",
-      metrics::CollaborationServiceShareOrManageEvent::kFlowRequirementsMet, 1);
+      "CollaborationService.ShareOrManageFlow.Events",
+      metrics::CollaborationServiceFlowEvent::kFlowRequirementsMet, 1);
   histogram_tester.ExpectBucketCount(
       "CollaborationService.ShareOrManageFlow",
       metrics::CollaborationServiceShareOrManageEvent::kManageDialogShown, 1);
@@ -909,14 +909,14 @@
 
   // Verify the not signed in metrics are recorded properly.
   histogram_tester.ExpectBucketCount(
-      "CollaborationService.ShareOrManageFlow",
-      metrics::CollaborationServiceShareOrManageEvent::kNotSignedIn, 1);
+      "CollaborationService.ShareOrManageFlow.Events",
+      metrics::CollaborationServiceFlowEvent::kNotSignedIn, 1);
   histogram_tester.ExpectBucketCount(
-      "CollaborationService.ShareOrManageFlow",
-      metrics::CollaborationServiceShareOrManageEvent::kCanceledNotSignedIn, 1);
+      "CollaborationService.ShareOrManageFlow.Events",
+      metrics::CollaborationServiceFlowEvent::kCanceledNotSignedIn, 1);
   histogram_tester.ExpectBucketCount(
-      "CollaborationService.ShareOrManageFlow",
-      metrics::CollaborationServiceShareOrManageEvent::kFlowRequirementsMet, 0);
+      "CollaborationService.ShareOrManageFlow.Events",
+      metrics::CollaborationServiceFlowEvent::kFlowRequirementsMet, 0);
 }
 
 TEST_F(CollaborationControllerTest, LeaveFlow) {
diff --git a/components/collaboration/internal/collaboration_service_impl.cc b/components/collaboration/internal/collaboration_service_impl.cc
index 4faa78ee..53cd3ba 100644
--- a/components/collaboration/internal/collaboration_service_impl.cc
+++ b/components/collaboration/internal/collaboration_service_impl.cc
@@ -36,8 +36,6 @@
 using data_sharing::GroupToken;
 using data_sharing::MemberRole;
 using Flow = CollaborationController::Flow;
-using metrics::CollaborationServiceJoinEvent;
-using metrics::CollaborationServiceShareOrManageEvent;
 using Outcome = signin::AccountManagedStatusFinder::Outcome;
 using ParseUrlResult = data_sharing::ParseUrlResult;
 using ParseUrlStatus = data_sharing::ParseUrlStatus;
@@ -109,9 +107,6 @@
   CancelAllFlows(base::BindOnce(
       &CollaborationServiceImpl::StartJoinFlowInternal,
       weak_ptr_factory_.GetWeakPtr(), std::move(delegate), token));
-
-  RecordJoinEvent(data_sharing_service_->GetLogger(),
-                  CollaborationServiceJoinEvent::kStarted);
 }
 
 void CollaborationServiceImpl::StartShareOrManageFlow(
@@ -126,9 +121,6 @@
       base::BindOnce(&CollaborationServiceImpl::StartCollaborationFlowInternal,
                      weak_ptr_factory_.GetWeakPtr(), std::move(delegate),
                      either_id, FlowType::kShareOrManage));
-
-  RecordShareOrManageEvent(data_sharing_service_->GetLogger(),
-                           CollaborationServiceShareOrManageEvent::kStarted);
 }
 
 void CollaborationServiceImpl::StartLeaveOrDeleteFlow(
diff --git a/components/collaboration/internal/messaging/instant_message_processor_impl.cc b/components/collaboration/internal/messaging/instant_message_processor_impl.cc
index 54a373f6..e22cc63 100644
--- a/components/collaboration/internal/messaging/instant_message_processor_impl.cc
+++ b/components/collaboration/internal/messaging/instant_message_processor_impl.cc
@@ -12,6 +12,7 @@
 #include "base/containers/contains.h"
 #include "base/functional/callback_helpers.h"
 #include "base/hash/hash.h"
+#include "base/strings/to_string.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "components/collaboration/public/messaging/message.h"
diff --git a/components/collaboration/internal/metrics.cc b/components/collaboration/internal/metrics.cc
index b5b3469..179376ca 100644
--- a/components/collaboration/internal/metrics.cc
+++ b/components/collaboration/internal/metrics.cc
@@ -283,6 +283,53 @@
   }
 }
 
+std::string_view CollaborationServiceFlowEventToString(
+    CollaborationServiceFlowEvent event) {
+  switch (event) {
+    case CollaborationServiceFlowEvent::kUnknown:
+      return "Unknown";
+    case CollaborationServiceFlowEvent::kStarted:
+      return "Started";
+    case CollaborationServiceFlowEvent::kNotSignedIn:
+      return "NotSignedIn";
+    case CollaborationServiceFlowEvent::kCanceledNotSignedIn:
+      return "CanceledNotSignedIn";
+    case CollaborationServiceFlowEvent::kFlowRequirementsMet:
+      return "FlowRequirementsMet";
+    case CollaborationServiceFlowEvent::kSigninVerificationFailed:
+      return "SigninVerificationFailed";
+    case CollaborationServiceFlowEvent::kSigninVerified:
+      return "SigninVerified";
+    case CollaborationServiceFlowEvent::kSigninVerifiedInObserver:
+      return "SigninVerifiedInObserver";
+    case CollaborationServiceFlowEvent::kDataSharingReadyWhenStarted:
+      return "DataSharingReadyWhenStarted";
+    case CollaborationServiceFlowEvent::kDataSharingServiceReadyObserved:
+      return "DataSharingServiceReadyObserved";
+    case CollaborationServiceFlowEvent::kTabGroupServiceReady:
+      return "TabGroupServiceReady";
+    case CollaborationServiceFlowEvent::kAllServicesReadyForFlow:
+      return "AllServicesReadyForFlow";
+    case CollaborationServiceFlowEvent::kDevicePolicyDisableSignin:
+      return "DevicePolicyDisableSignin";
+    case CollaborationServiceFlowEvent::kManagedAccountSignin:
+      return "ManagedAccountSignin";
+    case CollaborationServiceFlowEvent::kAccountInfoNotReadyOnSignin:
+      return "AccountInfoNotReadyOnSignin";
+  }
+}
+
+std::string_view CreateFlowTypeToString(FlowType type) {
+  switch (type) {
+    case FlowType::kJoin:
+      return "JoinFlow";
+    case FlowType::kShareOrManage:
+      return "ShareOrManageFlow";
+    case FlowType::kLeaveOrDelete:
+      return "LeaveOrDeleteFlow";
+  }
+}
+
 std::string CreateJoinEventLogString(CollaborationServiceJoinEvent event) {
   return base::StringPrintf("Join Flow Event: %s",
                             CollaborationServiceJoinEventToString(event));
@@ -314,6 +361,7 @@
       "Leave or Delete Flow Started\n  From: %s\n",
       CollaborationServiceLeaveOrDeleteEntryPointToString(entry));
 }
+
 std::string CreateLatencyLogToString(CollaborationServiceStep step,
                                      base::TimeDelta duration) {
   return base::StringPrintf("Step %s took %dms to complete.",
@@ -321,11 +369,15 @@
                             duration.InMillisecondsRoundedUp());
 }
 
+std::string CreateFlowEventLogString(CollaborationServiceFlowEvent event) {
+  return base::StringPrintf("Flow Event: %s",
+                            CollaborationServiceFlowEventToString(event));
+}
+
 }  // namespace
 
 void RecordJoinEvent(data_sharing::Logger* logger,
                      CollaborationServiceJoinEvent event) {
-  VLOG(1) << "RecordJoinEvent:" << (CreateJoinEventLogString(event));
   base::UmaHistogramEnumeration("CollaborationService.JoinFlow", event);
   DATA_SHARING_LOG(logger_common::mojom::LogSource::CollaborationService,
                    logger, CreateJoinEventLogString(event));
@@ -333,8 +385,6 @@
 
 void RecordShareOrManageEvent(data_sharing::Logger* logger,
                               CollaborationServiceShareOrManageEvent event) {
-  VLOG(1) << "RecordShareOrManageEvent:"
-          << (CreateShareOrManageEventLogString(event));
   base::UmaHistogramEnumeration("CollaborationService.ShareOrManageFlow",
                                 event);
   DATA_SHARING_LOG(logger_common::mojom::LogSource::CollaborationService,
@@ -348,7 +398,7 @@
     CollaborationServiceShareOrManageEvent share_or_manage_event) {
   if (type == FlowType::kJoin) {
     RecordJoinEvent(logger, join_event);
-  } else {
+  } else if (type == FlowType::kShareOrManage) {
     RecordShareOrManageEvent(logger, share_or_manage_event);
   }
 }
@@ -391,4 +441,15 @@
                    logger, CreateLatencyLogToString(step, duration));
 }
 
+void RecordCollaborationFlowEvent(data_sharing::Logger* logger,
+                                  FlowType type,
+                                  CollaborationServiceFlowEvent event) {
+  std::string histogram_name = base::StrCat(
+      {"CollaborationService.", CreateFlowTypeToString(type), ".Events"});
+
+  base::UmaHistogramEnumeration(histogram_name, event);
+  DATA_SHARING_LOG(logger_common::mojom::LogSource::CollaborationService,
+                   logger, CreateFlowEventLogString(event));
+}
+
 }  // namespace collaboration::metrics
diff --git a/components/collaboration/internal/metrics.h b/components/collaboration/internal/metrics.h
index a615251a..542b700 100644
--- a/components/collaboration/internal/metrics.h
+++ b/components/collaboration/internal/metrics.h
@@ -97,6 +97,30 @@
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/collaboration_service/enums.xml:CollaborationServiceShareOrManageEvent)
 
+// Types of collaboration flow events that occur in the collaboration service.
+// These values are persisted to logs. Entries should not be renumbered and
+// number values should never be reused.
+// LINT.IfChange(CollaborationServiceFlowEvent)
+enum class CollaborationServiceFlowEvent {
+  kUnknown = 0,
+  kStarted = 1,
+  kNotSignedIn = 2,
+  kCanceledNotSignedIn = 3,
+  kFlowRequirementsMet = 4,
+  kSigninVerificationFailed = 5,
+  kSigninVerified = 6,
+  kSigninVerifiedInObserver = 7,
+  kDataSharingReadyWhenStarted = 8,
+  kDataSharingServiceReadyObserved = 9,
+  kTabGroupServiceReady = 10,
+  kAllServicesReadyForFlow = 11,
+  kDevicePolicyDisableSignin = 12,
+  kManagedAccountSignin = 13,
+  kAccountInfoNotReadyOnSignin = 14,
+  kMaxValue = kAccountInfoNotReadyOnSignin,
+};
+// LINT.ThenChange(//tools/metrics/histograms/metadata/collaboration_service/enums.xml:CollaborationServiceFlowEvent)
+
 enum class CollaborationServiceStep {
   kUnknown = 0,
   kAuthenticationInitToSuccess = 1,
@@ -110,14 +134,14 @@
                      CollaborationServiceJoinEvent event);
 void RecordShareOrManageEvent(data_sharing::Logger* logger,
                               CollaborationServiceShareOrManageEvent event);
-void RecordLeaveOrDeleteEntryPoint(
-    data_sharing::Logger* logger,
-    CollaborationServiceLeaveOrDeleteEntryPoint event);
 void RecordJoinOrShareOrManageEvent(
     data_sharing::Logger* logger,
     FlowType type,
     CollaborationServiceJoinEvent join_event,
     CollaborationServiceShareOrManageEvent share_or_manage_event);
+void RecordLeaveOrDeleteEntryPoint(
+    data_sharing::Logger* logger,
+    CollaborationServiceLeaveOrDeleteEntryPoint event);
 void RecordJoinEntryPoint(data_sharing::Logger* logger,
                           CollaborationServiceJoinEntryPoint entry);
 void RecordShareOrManageEntryPoint(
@@ -126,6 +150,9 @@
 void RecordLatency(data_sharing::Logger* logger,
                    CollaborationServiceStep step,
                    base::TimeDelta duration);
+void RecordCollaborationFlowEvent(data_sharing::Logger* logger,
+                                  FlowType type,
+                                  CollaborationServiceFlowEvent event);
 }  // namespace collaboration::metrics
 
 #endif  // COMPONENTS_COLLABORATION_INTERNAL_METRICS_H_
diff --git a/components/embedder_support/android/util/android_stream_reader_url_loader.cc b/components/embedder_support/android/util/android_stream_reader_url_loader.cc
index 8521e3a..1e21760 100644
--- a/components/embedder_support/android/util/android_stream_reader_url_loader.cc
+++ b/components/embedder_support/android/util/android_stream_reader_url_loader.cc
@@ -20,7 +20,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/thread.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "components/embedder_support/android/util/features.h"
 #include "components/embedder_support/android/util/input_stream.h"
 #include "components/embedder_support/android/util/input_stream_reader.h"
diff --git a/components/exo/buffer.cc b/components/exo/buffer.cc
index abed326..27b2638 100644
--- a/components/exo/buffer.cc
+++ b/components/exo/buffer.cc
@@ -40,7 +40,6 @@
 #include "gpu/command_buffer/common/shared_image_capabilities.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/common/sync_token.h"
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/media_switches.h"
 #include "ui/aura/env.h"
 #include "ui/color/color_id.h"
diff --git a/components/exo/display.cc b/components/exo/display.cc
index 5d320fba..695d91f 100644
--- a/components/exo/display.cc
+++ b/components/exo/display.cc
@@ -31,7 +31,6 @@
 #include "components/exo/toast_surface.h"
 #include "components/exo/toast_surface_manager.h"
 #include "components/exo/xdg_shell_surface.h"
-#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
diff --git a/components/exo/shell_surface_util.h b/components/exo/shell_surface_util.h
index 79c96b74..1b24a327 100644
--- a/components/exo/shell_surface_util.h
+++ b/components/exo/shell_surface_util.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_EXO_SHELL_SURFACE_UTIL_H_
 #define COMPONENTS_EXO_SHELL_SURFACE_UTIL_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <optional>
 #include <string>
diff --git a/components/exo/wayland/test/resource_key.h b/components/exo/wayland/test/resource_key.h
index 11ae989..fe00953d 100644
--- a/components/exo/wayland/test/resource_key.h
+++ b/components/exo/wayland/test/resource_key.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_EXO_WAYLAND_TEST_RESOURCE_KEY_H_
 #define COMPONENTS_EXO_WAYLAND_TEST_RESOURCE_KEY_H_
 
+#include <stdint.h>
+
 #include <string>
 
 namespace exo::wayland::test {
diff --git a/components/exo/wayland/wayland_protocol_logger.h b/components/exo/wayland/wayland_protocol_logger.h
index 8eca06d..bea4d01 100644
--- a/components/exo/wayland/wayland_protocol_logger.h
+++ b/components/exo/wayland/wayland_protocol_logger.h
@@ -8,6 +8,7 @@
 #include <wayland-server-core.h>
 
 #include <memory>
+#include <string>
 #include <vector>
 
 namespace exo::wayland {
diff --git a/components/history/core/browser/sync/history_sync_bridge_unittest.cc b/components/history/core/browser/sync/history_sync_bridge_unittest.cc
index 9ea66ae..e1f842fc 100644
--- a/components/history/core/browser/sync/history_sync_bridge_unittest.cc
+++ b/components/history/core/browser/sync/history_sync_bridge_unittest.cc
@@ -13,6 +13,7 @@
 
 #include "base/notreached.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/history/core/browser/history_types.h"
diff --git a/components/language_detection/core/background_file_unittest.cc b/components/language_detection/core/background_file_unittest.cc
index 253e9bc..a52b8ba 100644
--- a/components/language_detection/core/background_file_unittest.cc
+++ b/components/language_detection/core/background_file_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/task/thread_pool.h"
 #include "base/test/run_until.h"
 #include "base/test/task_environment.h"
+#include "base/threading/thread_restrictions.h"
 #include "components/language_detection/testing/language_detection_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/memory_pressure/memory_pressure_voter.cc b/components/memory_pressure/memory_pressure_voter.cc
index e077fea..8826e51 100644
--- a/components/memory_pressure/memory_pressure_voter.cc
+++ b/components/memory_pressure/memory_pressure_voter.cc
@@ -8,7 +8,7 @@
 #include <optional>
 
 #include "base/memory/raw_ptr.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace memory_pressure {
 
diff --git a/components/memory_pressure/multi_source_memory_pressure_monitor.cc b/components/memory_pressure/multi_source_memory_pressure_monitor.cc
index 5415f57..1b857d0a 100644
--- a/components/memory_pressure/multi_source_memory_pressure_monitor.cc
+++ b/components/memory_pressure/multi_source_memory_pressure_monitor.cc
@@ -9,8 +9,8 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/memory_pressure_level_proto.h"
+#include "base/trace_event/trace_event.h"
 #include "base/tracing_buildflags.h"
 #include "components/memory_pressure/system_memory_pressure_evaluator.h"
 
diff --git a/components/omnibox/browser/tab_group_provider.cc b/components/omnibox/browser/tab_group_provider.cc
index 9e3f0b8..c11a632 100644
--- a/components/omnibox/browser/tab_group_provider.cc
+++ b/components/omnibox/browser/tab_group_provider.cc
@@ -9,7 +9,7 @@
 #include "base/i18n/case_conversion.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/saved_tab_groups/public/saved_tab_group.h"
 #include "components/saved_tab_groups/public/tab_group_sync_service.h"
diff --git a/components/page_load_metrics/google/browser/gws_page_load_metrics_observer.cc b/components/page_load_metrics/google/browser/gws_page_load_metrics_observer.cc
index 8c93bca..ca5a6d87 100644
--- a/components/page_load_metrics/google/browser/gws_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/google/browser/gws_page_load_metrics_observer.cc
@@ -17,8 +17,8 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/named_trigger.h"
+#include "base/trace_event/trace_event.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/page_load_metrics/browser/navigation_handle_user_data.h"
 #include "components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h"
diff --git a/components/persistent_cache/sqlite/sqlite_backend_impl.cc b/components/persistent_cache/sqlite/sqlite_backend_impl.cc
index 9d0dd59..8cf02a3 100644
--- a/components/persistent_cache/sqlite/sqlite_backend_impl.cc
+++ b/components/persistent_cache/sqlite/sqlite_backend_impl.cc
@@ -9,7 +9,7 @@
 
 #include "base/check_op.h"
 #include "base/containers/span.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "components/persistent_cache/sqlite/sqlite_entry_impl.h"
 #include "components/persistent_cache/sqlite/vfs/sandboxed_file.h"
 #include "components/persistent_cache/sqlite/vfs/sqlite_sandboxed_vfs.h"
diff --git a/components/policy/core/common/policy_map.cc b/components/policy/core/common/policy_map.cc
index e6d846d7f..7f91265 100644
--- a/components/policy/core/common/policy_map.cc
+++ b/components/policy/core/common/policy_map.cc
@@ -35,16 +35,18 @@
   std::u16string result = std::u16string();
   std::u16string line_feed = u"\n";
   for (const auto& string_pairs : localized_string_ids) {
-    if (string_pairs.second)
+    if (string_pairs.second) {
       result += l10n_util::GetStringFUTF16(
           string_pairs.first, string_pairs.second.value(), nullptr);
-    else
+    } else {
       result += lookup.Run(string_pairs.first);
+    }
     result += line_feed;
   }
   // Remove the trailing newline.
-  if (!result.empty() && result[result.length() - 1] == line_feed[0])
+  if (!result.empty() && result[result.length() - 1] == line_feed[0]) {
     result.pop_back();
+  }
   return result;
 }
 
@@ -71,21 +73,21 @@
     const PolicyDetails* details) {
   switch (source) {
     case POLICY_SOURCE_ENTERPRISE_DEFAULT:
-      return POLICY_PRIORITY_BROWSER_ENTERPRISE_DEFAULT;
+      return PolicyPriorityBrowser::kEnterpriseDefault;
     case POLICY_SOURCE_COMMAND_LINE:
-      return POLICY_PRIORITY_BROWSER_COMMAND_LINE;
+      return PolicyPriorityBrowser::kCommandLine;
     case POLICY_SOURCE_CLOUD:
       if (scope == POLICY_SCOPE_MACHINE) {
         // Raise the priority of cloud machine policies only when the metapolicy
         // CloudPolicyOverridesPlatformPolicy is set to true.
         return cloud_policy_overrides_platform_policy
-                   ? POLICY_PRIORITY_BROWSER_CLOUD_MACHINE_RAISED
-                   : POLICY_PRIORITY_BROWSER_CLOUD_MACHINE;
+                   ? PolicyPriorityBrowser::kCloudMachineRaised
+                   : PolicyPriorityBrowser::kCloudMachine;
       }
       // For policies that can only be set with managed account, raise the
       // priority of correct source to highest.
       if (details && details->scope == kSingleProfile) {
-        return POLICY_PRIORITY_BROWSER_CLOUD_USER_DOUBLE_RAISED;
+        return PolicyPriorityBrowser::kCloudUserDoubleRaised;
       }
       if (cloud_user_policy_overrides_cloud_machine_policy &&
           is_user_affiliated) {
@@ -94,16 +96,16 @@
         // user is affiliated. Its priority relative to cloud machine policies
         // also depends on the value of CloudPolicyOverridesPlatformPolicy.
         return cloud_policy_overrides_platform_policy
-                   ? POLICY_PRIORITY_BROWSER_CLOUD_USER_DOUBLE_RAISED
-                   : POLICY_PRIORITY_BROWSER_CLOUD_USER_RAISED;
+                   ? PolicyPriorityBrowser::kCloudUserDoubleRaised
+                   : PolicyPriorityBrowser::kCloudUserRaised;
       }
-      return POLICY_PRIORITY_BROWSER_CLOUD_USER;
+      return PolicyPriorityBrowser::kCloudUser;
     case POLICY_SOURCE_PLATFORM:
       return scope == POLICY_SCOPE_MACHINE
-                 ? POLICY_PRIORITY_BROWSER_PLATFORM_MACHINE
-                 : POLICY_PRIORITY_BROWSER_PLATFORM_USER;
+                 ? PolicyPriorityBrowser::kPlatformMachine
+                 : PolicyPriorityBrowser::kPlatformUser;
     case POLICY_SOURCE_MERGED:
-      return POLICY_PRIORITY_BROWSER_MERGED;
+      return PolicyPriorityBrowser::kMerged;
     default:
       NOTREACHED();
   }
@@ -180,9 +182,10 @@
 
 bool PolicyMap::Entry::Equals(const PolicyMap::Entry& other) const {
   bool conflicts_are_equal = conflicts.size() == other.conflicts.size();
-  for (size_t i = 0; conflicts_are_equal && i < conflicts.size(); ++i)
+  for (size_t i = 0; conflicts_are_equal && i < conflicts.size(); ++i) {
     conflicts_are_equal &=
         conflicts[i].entry().Equals(other.conflicts[i].entry());
+  }
 
   const bool equals =
       conflicts_are_equal && level == other.level && scope == other.scope &&
@@ -213,8 +216,9 @@
     return;
   }
   message_ids_[type].erase(message_id);
-  if (message_ids_[type].size() == 0)
+  if (message_ids_[type].size() == 0) {
     message_ids_.erase(type);
+  }
 }
 
 void PolicyMap::Entry::AddConflictingPolicy(Entry&& conflict) {
@@ -462,8 +466,9 @@
                             const PolicyMap& other,
                             bool using_default_precedence) {
   const Entry* other_policy = other.GetUntrusted(policy_name);
-  if (!other_policy)
+  if (!other_policy) {
     return;
+  }
 
   Entry* policy = GetMutableUntrusted(policy_name);
   Entry other_policy_copy = other_policy->DeepCopy();
@@ -498,8 +503,9 @@
     higher_policy.AddConflictingPolicy(std::move(conflicting_policy));
   }
 
-  if (other_is_higher_priority)
+  if (other_is_higher_priority) {
     *policy = std::move(other_policy_copy);
+  }
 }
 
 void PolicyMap::MergeFrom(const PolicyMap& other) {
@@ -538,8 +544,9 @@
 }
 
 void PolicyMap::MergeValues(const std::vector<PolicyMerger*>& mergers) {
-  for (const auto* it : mergers)
+  for (const auto* it : mergers) {
     it->Merge(this);
+  }
 }
 
 void PolicyMap::set_chrome_policy_details_callback_for_test(
@@ -549,8 +556,9 @@
 
 bool PolicyMap::IsPolicyExternal(const std::string& policy) {
   const PolicyDetails* policy_details = GetPolicyDetails(policy);
-  if (policy_details && policy_details->max_external_data_size > 0)
+  if (policy_details && policy_details->max_external_data_size > 0) {
     return true;
+  }
   return false;
 }
 
diff --git a/components/policy/core/common/policy_types.h b/components/policy/core/common/policy_types.h
index ccef8196..0755d18 100644
--- a/components/policy/core/common/policy_types.h
+++ b/components/policy/core/common/policy_types.h
@@ -82,37 +82,37 @@
 // priority, so source is used directly in the priority comparison.
 enum PolicyPriorityBrowser {
   // The policy was set through remapping or debugging.
-  POLICY_PRIORITY_BROWSER_ENTERPRISE_DEFAULT,
+  kEnterpriseDefault,
 
   // The policy was set by command line flag for testing purposes.
-  POLICY_PRIORITY_BROWSER_COMMAND_LINE,
+  kCommandLine,
 
   // The policy was set by a cloud source at the user level.
-  POLICY_PRIORITY_BROWSER_CLOUD_USER,
+  kCloudUser,
 
   // The policy was set by a platform source at the user level.
-  POLICY_PRIORITY_BROWSER_PLATFORM_USER,
+  kPlatformUser,
 
   // The policy was set by a cloud source at the machine level.
-  POLICY_PRIORITY_BROWSER_CLOUD_MACHINE,
+  kCloudMachine,
 
   // The policy was set by a cloud source at the user level. Its priority is
   // raised above that of cloud machine policies.
-  POLICY_PRIORITY_BROWSER_CLOUD_USER_RAISED,
+  kCloudUserRaised,
 
   // The policy was set by a platform source at the machine level.
-  POLICY_PRIORITY_BROWSER_PLATFORM_MACHINE,
+  kPlatformMachine,
 
   // The policy was set by a platform source at the machine level. Its priority
   // is raised above that of platform machine policies.
-  POLICY_PRIORITY_BROWSER_CLOUD_MACHINE_RAISED,
+  kCloudMachineRaised,
 
   // The policy was set by a cloud source at the user level. Its priority is
   // raised above that of platform and cloud machine policies.
-  POLICY_PRIORITY_BROWSER_CLOUD_USER_DOUBLE_RAISED,
+  kCloudUserDoubleRaised,
 
   // The policy coming from multiple sources and its value has been merged.
-  POLICY_PRIORITY_BROWSER_MERGED,
+  kMerged,
 };
 
 // The `PolicyFetchReason` enum is used to tag requests to DMServer. This allows
diff --git a/components/services/on_device_translation/public/cpp/features.cc b/components/services/on_device_translation/public/cpp/features.cc
index 2c8eeda..e95bd745 100644
--- a/components/services/on_device_translation/public/cpp/features.cc
+++ b/components/services/on_device_translation/public/cpp/features.cc
@@ -15,6 +15,10 @@
 
 namespace {
 
+// Limit the number of downloadable language packs to 5 during OT to mitigate
+// the risk of fingerprinting attacks.
+constexpr size_t kTranslationAPILimitLanguagePackCountMax = 5;
+
 base::FilePath GetPathFromCommandLine(const char* switch_name) {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line->HasSwitch(switch_name)) {
@@ -29,6 +33,14 @@
     &blink::features::kTranslationAPI, "TranslationAPILibraryMinimumVersion",
     "2025.1.10.0"};
 
+const base::FeatureParam<bool> kTranslationAPIAcceptLanguagesCheck{
+    &blink::features::kTranslationAPI, "TranslationAPIAcceptLanguagesCheck",
+    true};
+
+const base::FeatureParam<bool> kTranslationAPILimitLanguagePackCount{
+    &blink::features::kTranslationAPI, "TranslationAPILimitLanguagePackCount",
+    true};
+
 const base::FeatureParam<base::TimeDelta> kTranslationAPIServiceIdleTimeout{
     &blink::features::kTranslationAPI, "TranslationAPIServiceIdleTimeout",
     base::Minutes(1)};
@@ -41,6 +53,17 @@
   return GetPathFromCommandLine(kTranslateKitBinaryPath);
 }
 
+size_t GetInstallablePackageCount(size_t installed_package_count) {
+  if (base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) ||
+      !kTranslationAPILimitLanguagePackCount.Get()) {
+    return std::numeric_limits<size_t>::max();
+  }
+  if (installed_package_count >= kTranslationAPILimitLanguagePackCountMax) {
+    return 0;
+  }
+  return kTranslationAPILimitLanguagePackCountMax - installed_package_count;
+}
+
 bool IsValidTranslateKitVersion(std::string_view version_str) {
   base::Version minimum_version(kTranslationAPILibraryMinimumVersion.Get());
   CHECK(minimum_version.IsValid());
diff --git a/components/services/on_device_translation/public/cpp/features.h b/components/services/on_device_translation/public/cpp/features.h
index 279e820..1a80c7d 100644
--- a/components/services/on_device_translation/public/cpp/features.h
+++ b/components/services/on_device_translation/public/cpp/features.h
@@ -23,6 +23,18 @@
 // Any version string longer than this will be truncated.
 inline constexpr size_t kTranslationAPILibraryVersionStringSize = 14;
 
+// When this feature param is enabled, the Translation API will fail if neither
+// the source nor destination language is in the AcceptLanguages. This is
+// introduced to mitigate privacy concerns.
+extern const base::FeatureParam<bool> kTranslationAPIAcceptLanguagesCheck;
+
+// This feature limits the number of language components downloaded by
+// createTranslator() to 3.
+extern const base::FeatureParam<bool> kTranslationAPILimitLanguagePackCount;
+
+// Returns the number of additionally installable language packs.
+size_t GetInstallablePackageCount(size_t installed_package_count);
+
 // The duration that the OnDeviceTranslation service can remain idle before it
 // is terminated.
 extern const base::FeatureParam<base::TimeDelta>
diff --git a/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc b/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc
index 3bba8e44..2cd6819 100644
--- a/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc
+++ b/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "base/test/bind.h"
diff --git a/components/tracing/common/background_tracing_utils_unittest.cc b/components/tracing/common/background_tracing_utils_unittest.cc
index f8633df7..a250ddb 100644
--- a/components/tracing/common/background_tracing_utils_unittest.cc
+++ b/components/tracing/common/background_tracing_utils_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_proto_loader.h"
+#include "base/threading/thread_restrictions.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/tracing/common/background_tracing_state_manager.h"
 #include "components/tracing/common/pref_names.h"
diff --git a/components/update_client/background_downloader_mac_unittest.cc b/components/update_client/background_downloader_mac_unittest.cc
index 04e42dd..b55712c 100644
--- a/components/update_client/background_downloader_mac_unittest.cc
+++ b/components/update_client/background_downloader_mac_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
+#include "base/hash/hash.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
diff --git a/components/viz/common/overlay_state/win/overlay_state_service.cc b/components/viz/common/overlay_state/win/overlay_state_service.cc
index e155da6e..2248b58 100644
--- a/components/viz/common/overlay_state/win/overlay_state_service.cc
+++ b/components/viz/common/overlay_state/win/overlay_state_service.cc
@@ -10,7 +10,6 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/trace_event.h"
 
 namespace viz {
diff --git a/components/viz/host/gpu_client.cc b/components/viz/host/gpu_client.cc
index 2c81a757..e85a064 100644
--- a/components/viz/host/gpu_client.cc
+++ b/components/viz/host/gpu_client.cc
@@ -15,7 +15,6 @@
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h"
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "services/viz/privileged/mojom/gl/gpu_service.mojom.h"
 
 namespace viz {
diff --git a/components/viz/host/gpu_host_impl.cc b/components/viz/host/gpu_host_impl.cc
index 9900a93..b40c4bd 100644
--- a/components/viz/host/gpu_host_impl.cc
+++ b/components/viz/host/gpu_host_impl.cc
@@ -157,6 +157,7 @@
   viz_main_->CreateGpuService(
       gpu_service_remote_.BindNewPipeAndPassReceiver(task_runner),
       gpu_host_receiver_.BindNewPipeAndPassRemote(task_runner),
+      gpu_logging_receiver_.BindNewPipeAndPassRemote(task_runner),
       std::move(discardable_manager_remote),
       use_shader_cache_shm_count_.CloneRegion(), std::move(gpu_service_params));
   MaybeSendFontRenderParams();
@@ -640,12 +641,6 @@
   cache->Cache(key, blob);
 }
 
-void GpuHostImpl::RecordLogMessage(int32_t severity,
-                                   const std::string& header,
-                                   const std::string& message) {
-  delegate_->RecordLogMessage(severity, header, message);
-}
-
 void GpuHostImpl::ClearGrShaderDiskCache() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -662,6 +657,12 @@
   }
 }
 
+void GpuHostImpl::RecordLogMessage(int32_t severity,
+                                   const std::string& header,
+                                   const std::string& message) {
+  delegate_->RecordLogMessage(severity, header, message);
+}
+
 #if BUILDFLAG(USE_VIZ_DEBUGGER)
 void GpuHostImpl::LogFrame(base::Value frame_data) {
   if (!viz_debug_output_callback_.is_null())
diff --git a/components/viz/host/gpu_host_impl.h b/components/viz/host/gpu_host_impl.h
index ff6bba16..89b59cd6 100644
--- a/components/viz/host/gpu_host_impl.h
+++ b/components/viz/host/gpu_host_impl.h
@@ -38,6 +38,7 @@
 #include "services/service_manager/public/mojom/service.mojom.h"
 #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h"
 #include "services/viz/privileged/mojom/gl/gpu_host.mojom.h"
+#include "services/viz/privileged/mojom/gl/gpu_logging.mojom.h"
 #include "services/viz/privileged/mojom/gl/gpu_service.mojom.h"
 #include "services/viz/privileged/mojom/viz_main.mojom.h"
 #include "ui/gfx/gpu_extra_info.h"
@@ -59,7 +60,8 @@
 
 namespace viz {
 
-class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost
+class VIZ_HOST_EXPORT GpuHostImpl : public mojom::GpuHost,
+                                    public mojom::GpuLogging
 #if BUILDFLAG(USE_VIZ_DEBUGGER)
     ,
                                     public mojom::VizDebugOutput
@@ -276,10 +278,12 @@
   void StoreBlobToDisk(const gpu::GpuDiskCacheHandle& handle,
                        const std::string& key,
                        const std::string& blob) override;
+  void ClearGrShaderDiskCache() override;
+
+  // mojom::GpuLogging:
   void RecordLogMessage(int32_t severity,
                         const std::string& header,
                         const std::string& message) override;
-  void ClearGrShaderDiskCache() override;
 
   // Implements mojom::VizDebugOutput and is called by VizDebugger.
 #if BUILDFLAG(USE_VIZ_DEBUGGER)
@@ -298,6 +302,8 @@
       info_collection_gpu_service_remote_;
 #endif
   mojo::Receiver<mojom::GpuHost> gpu_host_receiver_{this};
+  mojo::Receiver<mojom::GpuLogging> gpu_logging_receiver_{this};
+
   gpu::GpuProcessHostShmCount use_shader_cache_shm_count_;
 
 #if BUILDFLAG(USE_VIZ_DEBUGGER)
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index a5e9ffc..f1b1fed3e 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -29,6 +29,7 @@
 #include "base/timer/elapsed_timer.h"
 #include "base/trace_event/common/trace_event_common.h"
 #include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_id_helper.h"
 #include "base/trace_event/traced_value.h"
 #include "base/trace_event/typed_macros.h"
 #include "build/build_config.h"
diff --git a/components/viz/service/gl/exit_code.cc b/components/viz/service/gl/exit_code.cc
index 8a3dc20..251482b3 100644
--- a/components/viz/service/gl/exit_code.cc
+++ b/components/viz/service/gl/exit_code.cc
@@ -5,13 +5,16 @@
 #include "components/viz/service/gl/exit_code.h"
 
 #include "base/logging.h"
-#include "base/process/process.h"
+#include "components/viz/service/gl/gpu_log_message_manager.h"
 
 namespace viz {
 
 void RestartGpuProcessForContextLoss(std::string_view reason) {
   LOG(ERROR) << "Restarting GPU process due to unrecoverable error. " << reason;
-  base::Process::TerminateCurrentProcessImmediately(
+
+  // Terminate the GPU process on IO thread to ensure previous mojo messages are
+  // in message queue.
+  GpuLogMessageManager::GetInstance()->TerminateProcess(
       static_cast<int>(ExitCode::RESULT_CODE_GPU_EXIT_ON_CONTEXT_LOST));
 }
 
diff --git a/components/viz/service/gl/gpu_log_message_manager.cc b/components/viz/service/gl/gpu_log_message_manager.cc
index 9e530a5d..26f56e4 100644
--- a/components/viz/service/gl/gpu_log_message_manager.cc
+++ b/components/viz/service/gl/gpu_log_message_manager.cc
@@ -4,7 +4,8 @@
 
 #include "components/viz/service/gl/gpu_log_message_manager.h"
 
-#include "services/viz/privileged/mojom/gl/gpu_host.mojom.h"
+#include "base/process/process.h"
+#include "base/synchronization/waitable_event.h"
 
 namespace viz {
 
@@ -43,55 +44,113 @@
                                               const std::string& header,
                                               const std::string& message) {
   base::AutoLock lock(message_lock_);
-  // During InstallPostInitializeLogHandler() there's a brief window where a
-  // call into this function may be waiting on |message_lock_|, so we need to
-  // check if |log_callback_| was set once we get the lock.
-  if (log_callback_) {
-    RouteMessage(severity, std::move(header), std::move(message));
-    return;
-  }
-
-  // Otherwise just queue the message for InstallPostInitializeLogHandler() to
-  // forward later.
-  deferred_messages_.emplace_back(severity, std::move(header),
-                                  std::move(message));
+  deferred_messages_.emplace_back(severity, header, message);
 }
 
 void GpuLogMessageManager::RouteMessage(int severity,
                                         const std::string& header,
                                         const std::string& message) {
-  log_callback_.Run(severity, std::move(header), std::move(message));
+  // This can be run from any thread, but mojo messages are sent on the IO
+  // thread.
+  if (io_task_runner_->BelongsToCurrentThread()) {
+    if (!gpu_logging_.is_bound()) {
+      // |InstallPostInitializeLogHandler| set a new log message handler, which
+      // will call |RouteMessage|. When |RouteMessage| handling log message
+      // GPULogging may not yet be bound. Cache those log messages until the
+      // Remote is bound.
+      base::AutoLock lock(message_lock_);
+      deferred_messages_.emplace_back(severity, header, message);
+      return;
+    }
+
+    gpu_logging_->RecordLogMessage(severity, header, message);
+  } else {
+    // Unretained is safe because |this| is valid until the process exits.
+    io_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&GpuLogMessageManager::RouteMessage,
+                                  base::Unretained(this), severity,
+                                  std::move(header), std::move(message)));
+  }
 }
 
-void GpuLogMessageManager::FlushMessages(mojom::GpuHost* gpu_host) {
+void GpuLogMessageManager::FlushMessages(mojom::GpuLogging* gpu_logging) {
   base::AutoLock lock(message_lock_);
   for (auto& log : deferred_messages_) {
-    gpu_host->RecordLogMessage(log.severity, std::move(log.header),
-                               std::move(log.message));
+    gpu_logging->RecordLogMessage(log.severity, std::move(log.header),
+                                  std::move(log.message));
   }
   deferred_messages_.clear();
 }
 
 void GpuLogMessageManager::InstallPreInitializeLogHandler() {
-  DCHECK(!log_callback_);
   logging::SetLogMessageHandler(PreInitializeLogHandler);
 }
 
 void GpuLogMessageManager::InstallPostInitializeLogHandler(
-    LogCallback log_callback) {
-  base::AutoLock lock(message_lock_);
-  DCHECK(!log_callback_);
-  log_callback_ = std::move(log_callback);
-  for (auto& log : deferred_messages_) {
-    RouteMessage(log.severity, std::move(log.header), std::move(log.message));
-  }
-  deferred_messages_.clear();
+    mojo::PendingRemote<mojom::GpuLogging> pending_remote,
+    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
+  Bind(std::move(pending_remote), std::move(io_task_runner));
+
   logging::SetLogMessageHandler(PostInitializeLogHandler);
 }
 
 void GpuLogMessageManager::ShutdownLogging() {
   logging::SetLogMessageHandler(nullptr);
-  log_callback_.Reset();
+
+  // |io_task_runner_| may be null if GPULogMessageManager hasn't been bound.
+  // Destroy the remote on the IO thread.
+  // base::Unretained is safe because this is a singleton.
+  if (io_task_runner_) {
+    io_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&GpuLogMessageManager::ResetLoggingOnIOThread,
+                                  base::Unretained(this)));
+  }
+}
+
+void GpuLogMessageManager::Bind(
+    mojo::PendingRemote<mojom::GpuLogging> pending_remote,
+    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
+  DCHECK(!io_task_runner->BelongsToCurrentThread());
+  io_task_runner_ = std::move(io_task_runner);
+
+  // base::Unretained is safe because this is a singleton.
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&GpuLogMessageManager::BindOnIOThread,
+                     base::Unretained(this), std::move(pending_remote)));
+}
+
+void GpuLogMessageManager::BindOnIOThread(
+    mojo::PendingRemote<mojom::GpuLogging> pending_remote) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  DCHECK(!gpu_logging_.is_bound());
+  gpu_logging_.Bind(std::move(pending_remote));
+
+  FlushMessages(gpu_logging_.get());
+}
+
+void GpuLogMessageManager::ResetLoggingOnIOThread() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  gpu_logging_.reset();
+}
+
+void GpuLogMessageManager::TerminateProcess(int exit_code) {
+  // Block the calling thread so it doesn't execute any more code before the
+  // process exits. This function cannot be called from the IO thread.
+  CHECK(!io_task_runner_->BelongsToCurrentThread());
+  base::WaitableEvent wait;
+
+  // base::Unretained is safe because this is a singleton.
+  io_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&GpuLogMessageManager::TerminateProcessOnIO,
+                                base::Unretained(this), exit_code));
+
+  wait.Wait();
+}
+
+void GpuLogMessageManager::TerminateProcessOnIO(int exit_code) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  base::Process::TerminateCurrentProcessImmediately(exit_code);
 }
 
 }  // namespace viz
diff --git a/components/viz/service/gl/gpu_log_message_manager.h b/components/viz/service/gl/gpu_log_message_manager.h
index 663c1b13..f8dc774 100644
--- a/components/viz/service/gl/gpu_log_message_manager.h
+++ b/components/viz/service/gl/gpu_log_message_manager.h
@@ -12,62 +12,73 @@
 #include "base/functional/callback.h"
 #include "base/no_destructor.h"
 #include "base/synchronization/lock.h"
+#include "base/task/single_thread_task_runner.h"
 #include "components/viz/service/viz_service_export.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/viz/privileged/mojom/gl/gpu_logging.mojom.h"
 
 namespace viz {
-
-namespace mojom {
-class GpuHost;
-}
-
-// Class which manages LOG() message forwarding before and after GpuServiceImpl
-// InitializeWithHost(). Prior to initialize, log messages are deferred and kept
-// within the class. During initialize, InstallPostInitializeLogHandler() will
-// be called to flush deferred messages and route new ones directly to GpuHost.
+// This class is a singleton which manages LOG() message forwarding before and
+// after GpuServiceImpl InitializeWithHost(). Prior to initialize, log messages
+// are deferred and kept within the class. When initialized GpuServiceImpl,
+// InstallPostInitializeLogHandler() will be called to flush deferred messages
+// and route new ones directly to browser. This class also makes sure the
+// logging mojo interface is bound to the IO thread and terminate process on the
+// IO thread.
 class VIZ_SERVICE_EXPORT GpuLogMessageManager {
  public:
-  using LogCallback = base::RepeatingCallback<void(int severity,
-                                                   const std::string& header,
-                                                   const std::string& message)>;
-
   static GpuLogMessageManager* GetInstance();
 
   GpuLogMessageManager(const GpuLogMessageManager&) = delete;
   GpuLogMessageManager& operator=(const GpuLogMessageManager&) = delete;
 
   // Queues a deferred LOG() message into |deferred_messages_| unless
-  // |log_callback_| has been set -- in which case RouteMessage() is called.
+  // |should_route_messages_| has been set to true -- in which case
+  // RouteMessage() is called.
   void AddDeferredMessage(int severity,
                           const std::string& header,
                           const std::string& message);
 
   // Used after InstallPostInitializeLogHandler() to route messages directly to
-  // |log_callback_|; avoids the need for a global lock.
+  // the GPU logging mojo interface, mojo interface send messages on IO thread.
   void RouteMessage(int severity,
                     const std::string& header,
                     const std::string& message);
 
   // If InstallPostInitializeLogHandler() will never be called, this method is
   // called prior to process exit to ensure logs are forwarded.
-  void FlushMessages(mojom::GpuHost* gpu_host);
+  void FlushMessages(mojom::GpuLogging* gpu_logging);
 
-  // Used prior to InitializeWithHost() during GpuMain startup to ensure logs
-  // aren't lost before initialize.
+  // Used prior to GpuServiceImpl initialization during GpuMain startup to
+  // ensure logs aren't lost before initialize.
   void InstallPreInitializeLogHandler();
 
-  // Called when GPUService created to take over logging from the
+  // Called when GpuServiceImpl is initialized, to take over logging from the
   // PostInitializeLogHandler(). Flushes all deferred messages.
-  void InstallPostInitializeLogHandler(LogCallback log_callback);
+  void InstallPostInitializeLogHandler(
+      mojo::PendingRemote<mojom::GpuLogging> pending_remote,
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
 
-  // Called when it's no longer safe to invoke |log_callback_|.
+  // Called when GpuServiceImpl is shutting down.
   void ShutdownLogging();
 
+  // Terminate the process on IO thread and block the main thread.
+  void TerminateProcess(int exit_code);
+
  private:
   friend class base::NoDestructor<GpuLogMessageManager>;
 
   GpuLogMessageManager();
   ~GpuLogMessageManager() = delete;
 
+  // Bind GpuLogging mojo interface to specified |task_runner|.
+  void Bind(mojo::PendingRemote<mojom::GpuLogging> pending_remote,
+            scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+  void BindOnIOThread(mojo::PendingRemote<mojom::GpuLogging> pending_remote);
+  void ResetLoggingOnIOThread();
+
+  void TerminateProcessOnIO(int exit_code);
+
   struct LogMessage {
     LogMessage(int severity,
                const std::string& header,
@@ -83,9 +94,9 @@
   base::Lock message_lock_;
   std::vector<LogMessage> deferred_messages_ GUARDED_BY(message_lock_);
 
-  // Set once under |mesage_lock_|, but may be accessed without lock after that.
-  LogCallback log_callback_;
+  mojo::Remote<mojom::GpuLogging> gpu_logging_;
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 };
-
 }  // namespace viz
+
 #endif  // COMPONENTS_VIZ_SERVICE_GL_GPU_LOG_MESSAGE_MANAGER_H_
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index ec870a83..9c0faf2 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -311,10 +311,6 @@
 
   bind_task_tracker_.TryCancelAll();
 
-  if (!in_host_process()) {
-    GpuLogMessageManager::GetInstance()->ShutdownLogging();
-  }
-
 #if BUILDFLAG(IS_WIN)
   gl::DirectCompositionOverlayCapsMonitor::GetInstance()->RemoveObserver(this);
 #endif
@@ -523,14 +519,6 @@
                           gpu_info_for_hardware_gpu_,
                           gpu_feature_info_for_hardware_gpu_, gpu_extra_info_);
   gpu_host_ = mojo::SharedRemote<mojom::GpuHost>(gpu_host.Unbind(), io_runner_);
-  if (!in_host_process()) {
-    // The global callback is reset from the dtor. So Unretained() here is safe.
-    // Note that the callback can be called from any thread. Consequently, the
-    // callback cannot use a WeakPtr.
-    GpuLogMessageManager::GetInstance()->InstallPostInitializeLogHandler(
-        base::BindRepeating(&GpuServiceImpl::RecordLogMessage,
-                            base::Unretained(this)));
-  }
 
   // Defer creation of the render thread. This is to prevent it from handling
   // IPC messages before the sandbox has been enabled and all other necessary
@@ -608,13 +596,6 @@
   visibility_changed_callback_ = std::move(callback);
 }
 
-void GpuServiceImpl::RecordLogMessage(int severity,
-                                      const std::string& header,
-                                      const std::string& message) {
-  // This can be run from any thread.
-  gpu_host_->RecordLogMessage(severity, std::move(header), std::move(message));
-}
-
 #if BUILDFLAG(IS_CHROMEOS)
 #if BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
 void GpuServiceImpl::CreateArcVideoDecodeAccelerator(
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index c38375d0..3b3e1f348 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -428,9 +428,6 @@
 
   bool IsNativeBufferSupported(gfx::BufferFormat format,
                                gfx::BufferUsage usage);
-  void RecordLogMessage(int severity,
-                        const std::string& header,
-                        const std::string& message);
 
 #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
   void CreateArcVideoDecodeAcceleratorOnMainThread(
diff --git a/components/viz/service/layers/layer_context_impl.cc b/components/viz/service/layers/layer_context_impl.cc
index ab02d8f6db..77eed595 100644
--- a/components/viz/service/layers/layer_context_impl.cc
+++ b/components/viz/service/layers/layer_context_impl.cc
@@ -48,6 +48,13 @@
 
 namespace {
 
+#define RETURN_IF_FALSE(expr, error)  \
+  do {                                \
+    if (!(expr)) {                    \
+      return base::unexpected(error); \
+    }                                 \
+  } while (false)
+
 int GenerateNextDisplayTreeId() {
   static int next_id = 1;
   return next_id++;
@@ -61,75 +68,92 @@
   return settings;
 }
 
-std::unique_ptr<cc::LayerImpl> CreateLayer(cc::LayerTreeHostImpl& host_impl,
-                                           cc::LayerTreeImpl& tree,
-                                           const mojom::Layer& wire) {
+base::expected<void, std::string> CreateLayer(
+    cc::LayerTreeHostImpl& host_impl,
+    cc::LayerTreeImpl& tree,
+    const mojom::Layer& wire,
+    std::unique_ptr<cc::LayerImpl>& layer) {
   cc::mojom::LayerType type = wire.type;
   int id = wire.id;
   switch (type) {
     case cc::mojom::LayerType::kLayer:
-      return cc::LayerImpl::Create(&tree, id);
+      layer = cc::LayerImpl::Create(&tree, id);
+      break;
 
     case cc::mojom::LayerType::kMirror:
-      return cc::MirrorLayerImpl::Create(&tree, id);
+      layer = cc::MirrorLayerImpl::Create(&tree, id);
+      break;
 
     case cc::mojom::LayerType::kNinePatchThumbScrollbar: {
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       auto& extra =
           wire.layer_extra->get_nine_patch_thumb_scrollbar_layer_extra();
       cc::ScrollbarOrientation orientation =
           extra->scrollbar_base_extra->is_horizontal_orientation
               ? cc::ScrollbarOrientation::kHorizontal
               : cc::ScrollbarOrientation::kVertical;
-      return cc::NinePatchThumbScrollbarLayerImpl::Create(
+      layer = cc::NinePatchThumbScrollbarLayerImpl::Create(
           &tree, id, orientation,
           extra->scrollbar_base_extra->is_left_side_vertical_scrollbar);
+      break;
     }
 
     case cc::mojom::LayerType::kPaintedScrollbar: {
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       auto& extra = wire.layer_extra->get_painted_scrollbar_layer_extra();
       cc::ScrollbarOrientation orientation =
           extra->scrollbar_base_extra->is_horizontal_orientation
               ? cc::ScrollbarOrientation::kHorizontal
               : cc::ScrollbarOrientation::kVertical;
-      return cc::PaintedScrollbarLayerImpl::Create(
+      layer = cc::PaintedScrollbarLayerImpl::Create(
           &tree, id, orientation,
           extra->scrollbar_base_extra->is_left_side_vertical_scrollbar,
           extra->scrollbar_base_extra->is_overlay_scrollbar);
+      break;
     }
 
     case cc::mojom::LayerType::kTileDisplay:
-      return std::make_unique<cc::TileDisplayLayerImpl>(tree, id);
+      layer = std::make_unique<cc::TileDisplayLayerImpl>(tree, id);
+      break;
 
     case cc::mojom::LayerType::kSolidColorScrollbar: {
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       auto& extra = wire.layer_extra->get_solid_color_scrollbar_layer_extra();
       cc::ScrollbarOrientation orientation =
           extra->scrollbar_base_extra->is_horizontal_orientation
               ? cc::ScrollbarOrientation::kHorizontal
               : cc::ScrollbarOrientation::kVertical;
-      return cc::SolidColorScrollbarLayerImpl::Create(
+      layer = cc::SolidColorScrollbarLayerImpl::Create(
           &tree, id, orientation, extra->thumb_thickness, extra->track_start,
           extra->scrollbar_base_extra->is_left_side_vertical_scrollbar);
+      break;
     }
 
     case cc::mojom::LayerType::kSurface:
       // The callback is triggered in the renderer side during WillDraw(),
       // and there is no need to do it in viz.
-      return cc::SurfaceLayerImpl::Create(&tree, id, base::NullCallback());
+      layer = cc::SurfaceLayerImpl::Create(&tree, id, base::NullCallback());
+      break;
 
     case cc::mojom::LayerType::kTexture:
-      return cc::TextureLayerImpl::Create(&tree, id);
+      layer = cc::TextureLayerImpl::Create(&tree, id);
+      break;
 
     case cc::mojom::LayerType::kViewTransitionContent: {
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       auto& extra = wire.layer_extra->get_view_transition_content_layer_extra();
-      return cc::ViewTransitionContentLayerImpl::Create(
+      layer = cc::ViewTransitionContentLayerImpl::Create(
           &tree, id, extra->resource_id, extra->is_live_content_layer,
           extra->max_extents_rect);
+      break;
     }
 
     default:
       // TODO(rockot): Support other layer types.
-      return cc::SolidColorLayerImpl::Create(&tree, id);
+      layer = cc::SolidColorLayerImpl::Create(&tree, id);
+      break;
   }
+  return base::ok();
 }
 
 template <typename TreeType>
@@ -574,6 +598,12 @@
 
 base::expected<void, std::string> UpdateLayer(const mojom::Layer& wire,
                                               cc::LayerImpl& layer) {
+  if (wire.contents_opaque && !wire.contents_opaque_for_text) {
+    return base::unexpected(
+        "Invalid contents_opaque_for_text: cannot be false if contents_opaque "
+        "is true.");
+  }
+
   layer.SetBounds(wire.bounds);
   layer.SetContentsOpaque(wire.contents_opaque);
   layer.SetContentsOpaqueForText(wire.contents_opaque_for_text);
@@ -640,33 +670,40 @@
 
   switch (wire.type) {
     case cc::mojom::LayerType::kMirror:
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       UpdateMirrorLayerExtra(wire.layer_extra->get_mirror_layer_extra(),
                              static_cast<cc::MirrorLayerImpl&>(layer));
       break;
     case cc::mojom::LayerType::kNinePatchThumbScrollbar:
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       UpdateNinePatchThumbScrollbarLayerExtra(
           wire.layer_extra->get_nine_patch_thumb_scrollbar_layer_extra(),
           static_cast<cc::NinePatchThumbScrollbarLayerImpl&>(layer));
       break;
     case cc::mojom::LayerType::kPaintedScrollbar:
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       UpdatePaintedScrollbarLayerExtra(
           wire.layer_extra->get_painted_scrollbar_layer_extra(),
           static_cast<cc::PaintedScrollbarLayerImpl&>(layer));
       break;
     case cc::mojom::LayerType::kSolidColorScrollbar:
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       UpdateSolidColorScrollbarLayerExtra(
           wire.layer_extra->get_solid_color_scrollbar_layer_extra(),
           static_cast<cc::SolidColorScrollbarLayerImpl&>(layer));
       break;
     case cc::mojom::LayerType::kSurface:
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       UpdateSurfaceLayerExtra(wire.layer_extra->get_surface_layer_extra(),
                               static_cast<cc::SurfaceLayerImpl&>(layer));
       break;
     case cc::mojom::LayerType::kTexture:
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       UpdateTextureLayerExtra(wire.layer_extra->get_texture_layer_extra(),
                               static_cast<cc::TextureLayerImpl&>(layer));
       break;
     case cc::mojom::LayerType::kViewTransitionContent:
+      RETURN_IF_FALSE(wire.layer_extra, "Invalid layer_extra");
       UpdateViewTransitionContentLayerExtra(
           wire.layer_extra->get_view_transition_content_layer_extra(),
           static_cast<cc::ViewTransitionContentLayerImpl&>(layer));
@@ -705,7 +742,7 @@
   for (auto& wire : updates) {
     auto& layer = layer_map[wire->id];
     if (!layer) {
-      layer = CreateLayer(host_impl, layers, *wire);
+      RETURN_IF_ERROR(CreateLayer(host_impl, layers, *wire, layer));
     }
     // TODO(crbug.com/418022040): Make sure we support re-creating Layers with
     // a previously used Id.
@@ -1429,10 +1466,18 @@
 
 void LayerContextImpl::UpdateDisplayTree(mojom::LayerTreeUpdatePtr update) {
   CHECK(receiver_);
+
+  const BeginFrameArgs begin_frame_args = update->begin_frame_args;
   auto result = DoUpdateDisplayTree(std::move(update));
   if (!result.has_value()) {
     receiver_->ReportBadMessage(result.error());
   }
+
+  // After a tree update, either Draw or schedule animations.
+  DoDraw(begin_frame_args);
+
+  // We may have resources to return after a tree update and draw.
+  DoReturnResources();
 }
 
 base::expected<void, std::string> LayerContextImpl::DoUpdateDisplayTree(
@@ -1652,28 +1697,26 @@
   RETURN_IF_ERROR(DeserializeAnimationUpdates(*update, *animation_host));
   host_impl_->ActivateAnimations();
 
+  return base::ok();
+}
+
+void LayerContextImpl::DoDraw(const BeginFrameArgs& begin_frame_args) {
   if (base::FeatureList::IsEnabled(features::kTreeAnimationsInViz)) {
     compositor_sink_->SetLayerContextWantsBeginFrames(true);
   } else {
     if (host_impl_->CanDraw()) {
-      host_impl_->WillBeginImplFrame(update->begin_frame_args);
+      host_impl_->WillBeginImplFrame(begin_frame_args);
 
       cc::LayerTreeHostImpl::FrameData frame;
       const bool has_damage = true;
-      frame.begin_frame_ack =
-          BeginFrameAck(update->begin_frame_args, has_damage);
-      frame.origin_begin_main_frame_args = update->begin_frame_args;
+      frame.begin_frame_ack = BeginFrameAck(begin_frame_args, has_damage);
+      frame.origin_begin_main_frame_args = begin_frame_args;
       host_impl_->PrepareToDraw(&frame);
       host_impl_->DrawLayers(&frame);
       host_impl_->DidDrawAllLayers(frame);
-      host_impl_->DidFinishImplFrame(update->begin_frame_args);
+      host_impl_->DidFinishImplFrame(begin_frame_args);
     }
   }
-
-  // We may have resources to return after a tree update and draw.
-  DoReturnResources();
-
-  return base::ok();
 }
 
 void LayerContextImpl::UpdateDisplayTiling(mojom::TilingPtr tiling,
diff --git a/components/viz/service/layers/layer_context_impl.h b/components/viz/service/layers/layer_context_impl.h
index dff049c..4f21211 100644
--- a/components/viz/service/layers/layer_context_impl.h
+++ b/components/viz/service/layers/layer_context_impl.h
@@ -57,6 +57,7 @@
 
   base::expected<void, std::string> DoUpdateDisplayTree(
       mojom::LayerTreeUpdatePtr update);
+  void DoDraw(const BeginFrameArgs& begin_frame_args);
 
   // Receive exported resources returned from the frame sink.
   void ReceiveReturnsFromParent(std::vector<ReturnedResource> resources);
diff --git a/components/viz/service/layers/layer_context_impl_unittest.cc b/components/viz/service/layers/layer_context_impl_unittest.cc
index 2669e30..362e0da 100644
--- a/components/viz/service/layers/layer_context_impl_unittest.cc
+++ b/components/viz/service/layers/layer_context_impl_unittest.cc
@@ -22,6 +22,7 @@
 #include "components/viz/test/fake_compositor_frame_sink_client.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "services/viz/public/mojom/compositing/layer_context.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -49,6 +50,12 @@
         compositor_frame_sink_support_.get(), /*draw_mode_is_gpu=*/true);
   }
 
+  void ResetTestState() {
+    // Property tree node IDs and layers are reinitialized in
+    // CreateDefaultUpdate if first_update_ is true.
+    first_update_ = true;
+  }
+
   mojom::LayerTreeUpdatePtr CreateDefaultUpdate() {
     auto update = mojom::LayerTreeUpdate::New();
 
@@ -102,6 +109,14 @@
   }
 
   void AddFirstTimeDefaultProperties(mojom::LayerTreeUpdate* update) {
+    // Set internal state to defaults.
+    next_layer_id_ = 1;
+    next_transform_id_ = 0;
+    next_clip_id_ = 0;
+    next_effect_id_ = 0;
+    next_scroll_id_ = 0;
+    layer_order_.clear();
+
     // Root & Secondary transform nodes are always expected 1st
     AddTransformNode(update, cc::kInvalidPropertyNodeId);
     AddTransformNode(update, cc::kRootPropertyNodeId);
@@ -256,7 +271,820 @@
 
 }  // namespace
 
-TEST_F(LayerContextImplTest, EmptyScrollingContentsCullRectsByDefault) {
+class LayerContextImplUpdateDisplayTreeTransformNodeTest
+    : public LayerContextImplTest {
+ protected:
+  cc::TransformNode* GetTransformNodeFromActiveTree(int node_id) {
+    if (node_id < static_cast<int>(layer_context_impl_->host_impl()
+                                       ->active_tree()
+                                       ->property_trees()
+                                       ->transform_tree()
+                                       .size())) {
+      return layer_context_impl_->host_impl()
+          ->active_tree()
+          ->property_trees()
+          ->transform_tree_mutable()
+          .Node(node_id);
+    }
+    return nullptr;
+  }
+};
+
+TEST_F(LayerContextImplUpdateDisplayTreeTransformNodeTest,
+       UpdateExistingTransformNodeProperties) {
+  // Apply a default valid update first.
+  auto update1 = CreateDefaultUpdate();
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+
+  auto update2 = CreateDefaultUpdate();
+  auto node_update = mojom::TransformNode::New();
+  node_update->id = cc::kSecondaryRootPropertyNodeId;
+  // Keep parent_id same as default.
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  node_update->local = gfx::Transform::MakeScale(2.0f);
+  node_update->origin = gfx::Point3F(1.f, 2.f, 3.f);
+  node_update->post_translation = gfx::Vector2dF(10.f, 20.f);
+  node_update->scroll_offset = gfx::PointF(5.f, 6.f);
+  node_update->sorting_context_id = 1;
+  node_update->flattens_inherited_transform = true;
+  node_update->will_change_transform = true;
+  node_update->damage_reasons_bit_mask =
+      (cc::DamageReasonSet{cc::DamageReason::kUntracked}).ToEnumBitmask();
+  node_update->moved_by_safe_area_bottom = true;
+
+  update2->transform_nodes.push_back(std::move(node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update2));
+  ASSERT_TRUE(result.has_value());
+
+  cc::TransformNode* node_impl =
+      GetTransformNodeFromActiveTree(cc::kSecondaryRootPropertyNodeId);
+  ASSERT_TRUE(node_impl);
+  EXPECT_EQ(node_impl->local, gfx::Transform::MakeScale(2.0f));
+  EXPECT_EQ(node_impl->origin, gfx::Point3F(1.f, 2.f, 3.f));
+  EXPECT_EQ(node_impl->post_translation, gfx::Vector2dF(10.f, 20.f));
+  EXPECT_EQ(node_impl->scroll_offset(), gfx::PointF(5.f, 6.f));
+  EXPECT_EQ(node_impl->sorting_context_id, 1);
+  EXPECT_TRUE(node_impl->flattens_inherited_transform);
+  EXPECT_TRUE(node_impl->will_change_transform);
+  EXPECT_TRUE(node_impl->damage_reasons().Has(cc::DamageReason::kUntracked));
+  EXPECT_TRUE(node_impl->moved_by_safe_area_bottom);
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeTransformNodeTest,
+       AddRemoveTransformNodes) {
+  // Apply a default valid update first.
+  auto update1 = CreateDefaultUpdate();
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+  uint32_t initial_node_count = layer_context_impl_->host_impl()
+                                    ->active_tree()
+                                    ->property_trees()
+                                    ->transform_tree()
+                                    .nodes()
+                                    .size();
+
+  // Add a new node.
+  auto update_add = CreateDefaultUpdate();
+  int new_node_id =
+      AddTransformNode(update_add.get(), cc::kSecondaryRootPropertyNodeId);
+  EXPECT_EQ(update_add->num_transform_nodes, initial_node_count + 1);
+
+  auto result_add =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_add));
+  ASSERT_TRUE(result_add.has_value());
+  EXPECT_EQ(layer_context_impl_->host_impl()
+                ->active_tree()
+                ->property_trees()
+                ->transform_tree()
+                .nodes()
+                .size(),
+            initial_node_count + 1);
+  cc::TransformNode* added_node_impl =
+      GetTransformNodeFromActiveTree(new_node_id);
+  ASSERT_TRUE(added_node_impl);
+  EXPECT_EQ(added_node_impl->parent_id, cc::kSecondaryRootPropertyNodeId);
+
+  // Remove the added node.
+  auto update_remove = CreateDefaultUpdate();
+  update_remove->num_transform_nodes = initial_node_count;
+  // To remove, we just send fewer nodes in num_transform_nodes.
+  // The actual nodes in transform_nodes vector can be empty or partial.
+  // Here we send an empty list for simplicity.
+  update_remove->transform_nodes.clear();
+
+  auto result_remove =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_remove));
+  ASSERT_TRUE(result_remove.has_value());
+  EXPECT_EQ(layer_context_impl_->host_impl()
+                ->active_tree()
+                ->property_trees()
+                ->transform_tree()
+                .nodes()
+                .size(),
+            initial_node_count);
+  EXPECT_FALSE(GetTransformNodeFromActiveTree(new_node_id));
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeTransformNodeTest,
+       UpdateTransformTreeProperties) {
+  auto update = CreateDefaultUpdate();
+  auto tree_props = mojom::TransformTreeUpdate::New();
+  tree_props->page_scale_factor = 1.5f;
+  tree_props->device_scale_factor = 2.0f;
+  tree_props->device_transform_scale_factor = 2.5f;
+  tree_props->nodes_affected_by_outer_viewport_bounds_delta = {
+      cc::kSecondaryRootPropertyNodeId};
+  tree_props->nodes_affected_by_safe_area_bottom = {
+      cc::kSecondaryRootPropertyNodeId};
+  update->transform_tree_update = std::move(tree_props);
+
+  // The top level page_scale_factor overrides whatever we set
+  // in the transform tree, so set it to the same value.
+  // TODO(vmiura): See if we could just remove syncing the
+  // transform tree scale factors?
+  update->page_scale_factor = 1.5f;
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_TRUE(result.has_value());
+
+  const auto& transform_tree = layer_context_impl_->host_impl()
+                                   ->active_tree()
+                                   ->property_trees()
+                                   ->transform_tree();
+  EXPECT_EQ(transform_tree.page_scale_factor(), 1.5f);
+  EXPECT_EQ(transform_tree.device_scale_factor(), 2.0f);
+  EXPECT_EQ(transform_tree.device_transform_scale_factor(), 2.5f);
+  EXPECT_THAT(transform_tree.nodes_affected_by_outer_viewport_bounds_delta(),
+              testing::ElementsAre(cc::kSecondaryRootPropertyNodeId));
+  EXPECT_THAT(transform_tree.nodes_affected_by_safe_area_bottom(),
+              testing::ElementsAre(cc::kSecondaryRootPropertyNodeId));
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeTransformNodeTest,
+       StickyPositionDataValid) {
+  auto update = CreateDefaultUpdate();
+  int scroll_node_id = AddScrollNode(update.get(), cc::kRootPropertyNodeId);
+
+  auto tree_props = mojom::TransformTreeUpdate::New();
+  auto sticky_data = mojom::StickyPositionNodeData::New();
+  sticky_data->scroll_ancestor = scroll_node_id;
+  sticky_data->is_anchored_top = true;
+  sticky_data->top_offset = 10.f;
+  tree_props->sticky_position_data.push_back(std::move(sticky_data));
+  update->transform_tree_update = std::move(tree_props);
+
+  // Add a transform node that will use this sticky data.
+  auto transform_node_update = mojom::TransformNode::New();
+  transform_node_update->id =
+      AddTransformNode(update.get(), cc::kRootPropertyNodeId);
+  transform_node_update->parent_id = cc::kRootPropertyNodeId;
+  transform_node_update->sticky_position_constraint_id = 0;
+  update->transform_nodes.push_back(std::move(transform_node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_TRUE(result.has_value());
+
+  const auto& transform_tree = layer_context_impl_->host_impl()
+                                   ->active_tree()
+                                   ->property_trees()
+                                   ->transform_tree();
+  ASSERT_EQ(transform_tree.sticky_position_data().size(), 1u);
+  EXPECT_EQ(transform_tree.sticky_position_data()[0].scroll_ancestor,
+            scroll_node_id);
+  EXPECT_TRUE(
+      transform_tree.sticky_position_data()[0].constraints.is_anchored_top);
+  EXPECT_EQ(transform_tree.sticky_position_data()[0].constraints.top_offset,
+            10.f);
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeTransformNodeTest,
+       StickyPositionDataInvalidScrollAncestor) {
+  auto update = CreateDefaultUpdate();
+  auto tree_props = mojom::TransformTreeUpdate::New();
+  auto sticky_data = mojom::StickyPositionNodeData::New();
+  sticky_data->scroll_ancestor = 99;  // Invalid scroll node ID
+  tree_props->sticky_position_data.push_back(std::move(sticky_data));
+  update->transform_tree_update = std::move(tree_props);
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid scroll ancestor ID");
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeTransformNodeTest,
+       AnchorPositionDataValid) {
+  auto update = CreateDefaultUpdate();
+  int adjustment_container_id =
+      AddTransformNode(update.get(), cc::kRootPropertyNodeId);
+
+  auto tree_props = mojom::TransformTreeUpdate::New();
+  auto anchor_data = mojom::AnchorPositionScrollData::New();
+  anchor_data->adjustment_container_ids.push_back(
+      cc::ElementId(adjustment_container_id));
+  anchor_data->accumulated_scroll_origin = gfx::Vector2d(5, 5);
+  tree_props->anchor_position_scroll_data.push_back(std::move(anchor_data));
+  update->transform_tree_update = std::move(tree_props);
+
+  // Add a transform node that will use this anchor data.
+  auto transform_node_update = mojom::TransformNode::New();
+  transform_node_update->id =
+      AddTransformNode(update.get(), cc::kRootPropertyNodeId);
+  transform_node_update->parent_id = cc::kRootPropertyNodeId;
+  transform_node_update->anchor_position_scroll_data_id = 0;
+  update->transform_nodes.push_back(std::move(transform_node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_TRUE(result.has_value());
+
+  const auto& transform_tree = layer_context_impl_->host_impl()
+                                   ->active_tree()
+                                   ->property_trees()
+                                   ->transform_tree();
+  ASSERT_EQ(transform_tree.anchor_position_scroll_data().size(), 1u);
+  EXPECT_THAT(
+      transform_tree.anchor_position_scroll_data()[0].adjustment_container_ids,
+      testing::ElementsAre(cc::ElementId(adjustment_container_id)));
+  EXPECT_EQ(
+      transform_tree.anchor_position_scroll_data()[0].accumulated_scroll_origin,
+      gfx::Vector2d(5, 5));
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeTransformNodeTest,
+       InvalidTransformNodeParentId) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::TransformNode::New();
+  node_update->id = next_transform_id_++;  // New node
+  node_update->parent_id = 99;             // Invalid parent ID
+  update->transform_nodes.push_back(std::move(node_update));
+  update->num_transform_nodes = next_transform_id_;
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid property tree node parent_id");
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeTransformNodeTest,
+       InvalidTransformNodeIdOnUpdate) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::TransformNode::New();
+  node_update->id = 99;  // Invalid node ID to update
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  update->transform_nodes.push_back(std::move(node_update));
+  // num_transform_nodes remains the same as default.
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid property tree node ID");
+}
+
+class LayerContextImplUpdateDisplayTreeClipNodeTest
+    : public LayerContextImplTest {
+ protected:
+  cc::ClipNode* GetClipNodeFromActiveTree(int node_id) {
+    if (node_id < static_cast<int>(layer_context_impl_->host_impl()
+                                       ->active_tree()
+                                       ->property_trees()
+                                       ->clip_tree()
+                                       .size())) {
+      return layer_context_impl_->host_impl()
+          ->active_tree()
+          ->property_trees()
+          ->clip_tree_mutable()
+          .Node(node_id);
+    }
+    return nullptr;
+  }
+};
+
+TEST_F(LayerContextImplUpdateDisplayTreeClipNodeTest,
+       UpdateExistingClipNodeProperties) {
+  // Apply a default valid update first.
+  auto update1 = CreateDefaultUpdate();
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+
+  auto update2 = CreateDefaultUpdate();
+  auto node_update = mojom::ClipNode::New();
+  node_update->id = cc::kSecondaryRootPropertyNodeId;
+  // Keep parent_id same as default.
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  node_update->clip = gfx::RectF(10.f, 20.f, 30.f, 40.f);
+  // Use a valid existing transform node ID.
+  node_update->transform_id = cc::kSecondaryRootPropertyNodeId;
+  update2->clip_nodes.push_back(std::move(node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update2));
+  ASSERT_TRUE(result.has_value());
+
+  cc::ClipNode* node_impl =
+      GetClipNodeFromActiveTree(cc::kSecondaryRootPropertyNodeId);
+  ASSERT_TRUE(node_impl);
+  EXPECT_EQ(node_impl->clip, gfx::RectF(10.f, 20.f, 30.f, 40.f));
+  EXPECT_EQ(node_impl->transform_id, cc::kSecondaryRootPropertyNodeId);
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeClipNodeTest, AddRemoveClipNodes) {
+  // Apply a default valid update first.
+  auto update1 = CreateDefaultUpdate();
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+  uint32_t initial_node_count = layer_context_impl_->host_impl()
+                                    ->active_tree()
+                                    ->property_trees()
+                                    ->clip_tree()
+                                    .nodes()
+                                    .size();
+
+  // Add a new node.
+  auto update_add = CreateDefaultUpdate();
+  int new_node_id =
+      AddClipNode(update_add.get(), cc::kSecondaryRootPropertyNodeId);
+  EXPECT_EQ(update_add->num_clip_nodes, initial_node_count + 1);
+
+  auto result_add =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_add));
+  ASSERT_TRUE(result_add.has_value());
+  EXPECT_EQ(layer_context_impl_->host_impl()
+                ->active_tree()
+                ->property_trees()
+                ->clip_tree()
+                .nodes()
+                .size(),
+            initial_node_count + 1);
+  cc::ClipNode* added_node_impl = GetClipNodeFromActiveTree(new_node_id);
+  ASSERT_TRUE(added_node_impl);
+  EXPECT_EQ(added_node_impl->parent_id, cc::kSecondaryRootPropertyNodeId);
+
+  // Remove the added node.
+  auto update_remove = CreateDefaultUpdate();
+  update_remove->num_clip_nodes = initial_node_count;
+  update_remove->clip_nodes.clear();
+
+  auto result_remove =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_remove));
+  ASSERT_TRUE(result_remove.has_value());
+  EXPECT_EQ(layer_context_impl_->host_impl()
+                ->active_tree()
+                ->property_trees()
+                ->clip_tree()
+                .nodes()
+                .size(),
+            initial_node_count);
+  EXPECT_FALSE(GetClipNodeFromActiveTree(new_node_id));
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeClipNodeTest, InvalidClipNodeParentId) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::ClipNode::New();
+  node_update->id = next_clip_id_++;  // New node
+  node_update->parent_id = 99;        // Invalid parent ID
+  node_update->transform_id = cc::kRootPropertyNodeId;
+  update->clip_nodes.push_back(std::move(node_update));
+  update->num_clip_nodes = next_clip_id_;
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid property tree node parent_id");
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeClipNodeTest,
+       InvalidClipNodeTransformId) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::ClipNode::New();
+  node_update->id = cc::kSecondaryRootPropertyNodeId;  // Existing node
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  node_update->transform_id = 99;  // Invalid transform ID
+  update->clip_nodes.push_back(std::move(node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid transform_id for clip node");
+}
+
+class LayerContextImplUpdateDisplayTreeEffectNodeTest
+    : public LayerContextImplTest {
+ protected:
+  cc::EffectNode* GetEffectNodeFromActiveTree(int node_id) {
+    if (node_id < static_cast<int>(layer_context_impl_->host_impl()
+                                       ->active_tree()
+                                       ->property_trees()
+                                       ->effect_tree()
+                                       .size())) {
+      return layer_context_impl_->host_impl()
+          ->active_tree()
+          ->property_trees()
+          ->effect_tree_mutable()
+          .Node(node_id);
+    }
+    return nullptr;
+  }
+};
+
+TEST_F(LayerContextImplUpdateDisplayTreeEffectNodeTest,
+       UpdateExistingEffectNodeProperties) {
+  // Apply a default valid update, with a new effect node.
+  auto update1 = CreateDefaultUpdate();
+  int effect_node_id =
+      AddEffectNode(update1.get(), cc::kSecondaryRootPropertyNodeId);
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+
+  auto update2 = CreateDefaultUpdate();
+  auto node_update = mojom::EffectNode::New();
+  node_update->id = effect_node_id;
+  // Keep parent_id same as default.
+  node_update->parent_id = cc::kSecondaryRootPropertyNodeId;
+  node_update->opacity = 0.5f;
+  node_update->filters.Append(cc::FilterOperation::CreateBlurFilter(2.f));
+  node_update->backdrop_filters.Append(
+      cc::FilterOperation::CreateGrayscaleFilter(0.8f));
+  node_update->blend_mode = static_cast<uint32_t>(SkBlendMode::kMultiply);
+  node_update->render_surface_reason = cc::RenderSurfaceReason::kTest;
+
+  // TODO(vmiura): If we have a render_surface_reason, without a valid
+  // element_id, we can trigger crashes during property tree update. Fix that.
+  node_update->element_id = cc::ElementId(42);
+
+  node_update->cache_render_surface = true;
+
+  const auto view_transition_token = blink::ViewTransitionToken();
+  node_update->view_transition_element_resource_id =
+      ViewTransitionElementResourceId(view_transition_token, 1, false);
+  // Use valid existing transform and clip node IDs.
+  node_update->transform_id = cc::kSecondaryRootPropertyNodeId;
+  node_update->clip_id = cc::kSecondaryRootPropertyNodeId;
+  node_update->target_id = cc::kRootPropertyNodeId;
+
+  update2->effect_nodes.push_back(std::move(node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update2));
+  ASSERT_TRUE(result.has_value());
+
+  cc::EffectNode* node_impl = GetEffectNodeFromActiveTree(effect_node_id);
+  ASSERT_TRUE(node_impl);
+  EXPECT_EQ(node_impl->opacity, 0.5f);
+  EXPECT_EQ(node_impl->filters.size(), 1u);
+  EXPECT_EQ(node_impl->filters.at(0).type(),
+            cc::FilterOperation::FilterType::BLUR);
+  EXPECT_EQ(node_impl->backdrop_filters.size(), 1u);
+  EXPECT_EQ(node_impl->backdrop_filters.at(0).type(),
+            cc::FilterOperation::FilterType::GRAYSCALE);
+  EXPECT_EQ(node_impl->blend_mode, SkBlendMode::kMultiply);
+  EXPECT_EQ(node_impl->render_surface_reason, cc::RenderSurfaceReason::kTest);
+  EXPECT_TRUE(node_impl->cache_render_surface);
+  EXPECT_EQ(node_impl->view_transition_element_resource_id,
+            ViewTransitionElementResourceId(view_transition_token, 1, false));
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeEffectNodeTest, AddRemoveEffectNodes) {
+  // Apply a default valid update first.
+  auto update1 = CreateDefaultUpdate();
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+  uint32_t initial_node_count = layer_context_impl_->host_impl()
+                                    ->active_tree()
+                                    ->property_trees()
+                                    ->effect_tree()
+                                    .nodes()
+                                    .size();
+
+  // Add a new node.
+  auto update_add = CreateDefaultUpdate();
+  int new_node_id =
+      AddEffectNode(update_add.get(), cc::kSecondaryRootPropertyNodeId);
+  EXPECT_EQ(update_add->num_effect_nodes, initial_node_count + 1);
+
+  auto result_add =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_add));
+  ASSERT_TRUE(result_add.has_value());
+  EXPECT_EQ(layer_context_impl_->host_impl()
+                ->active_tree()
+                ->property_trees()
+                ->effect_tree()
+                .nodes()
+                .size(),
+            initial_node_count + 1);
+  cc::EffectNode* added_node_impl = GetEffectNodeFromActiveTree(new_node_id);
+  ASSERT_TRUE(added_node_impl);
+  EXPECT_EQ(added_node_impl->parent_id, cc::kSecondaryRootPropertyNodeId);
+
+  // Remove the added node.
+  auto update_remove = CreateDefaultUpdate();
+  update_remove->num_effect_nodes = initial_node_count;
+  update_remove->effect_nodes.clear();
+
+  auto result_remove =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_remove));
+  ASSERT_TRUE(result_remove.has_value());
+  EXPECT_EQ(layer_context_impl_->host_impl()
+                ->active_tree()
+                ->property_trees()
+                ->effect_tree()
+                .nodes()
+                .size(),
+            initial_node_count);
+  EXPECT_FALSE(GetEffectNodeFromActiveTree(new_node_id));
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeEffectNodeTest,
+       AddRemoveCopyOutputRequests) {
+  // Apply a default valid update first.
+  auto update1 = CreateDefaultUpdate();
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+
+  // Add a copy request.
+  auto update_add_request = CreateDefaultUpdate();
+  auto node_update = mojom::EffectNode::New();
+  node_update->id = cc::kSecondaryRootPropertyNodeId;
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  node_update->transform_id = cc::kSecondaryRootPropertyNodeId;
+  node_update->clip_id = cc::kSecondaryRootPropertyNodeId;
+  node_update->target_id = cc::kRootPropertyNodeId;
+  node_update->copy_output_requests.push_back(
+      CopyOutputRequest::CreateStubForTesting());
+  update_add_request->effect_nodes.push_back(std::move(node_update));
+
+  auto result_add =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_add_request));
+  ASSERT_TRUE(result_add.has_value());
+  auto copy_requests = layer_context_impl_->host_impl()
+                           ->active_tree()
+                           ->property_trees()
+                           ->effect_tree_mutable()
+                           .TakeCopyRequests();
+  EXPECT_EQ(copy_requests.count(cc::kSecondaryRootPropertyNodeId), 1u);
+
+  // Remove the copy request (by not sending it).
+  auto update_remove_request = CreateDefaultUpdate();
+  auto node_update_no_request = mojom::EffectNode::New();
+  node_update_no_request->id = cc::kSecondaryRootPropertyNodeId;
+  node_update_no_request->parent_id = cc::kRootPropertyNodeId;
+  node_update_no_request->transform_id = cc::kSecondaryRootPropertyNodeId;
+  node_update_no_request->clip_id = cc::kSecondaryRootPropertyNodeId;
+  node_update_no_request->target_id = cc::kRootPropertyNodeId;
+  update_remove_request->effect_nodes.push_back(
+      std::move(node_update_no_request));
+
+  auto result_remove = layer_context_impl_->DoUpdateDisplayTree(
+      std::move(update_remove_request));
+  ASSERT_TRUE(result_remove.has_value());
+  auto copy_requests_after_remove = layer_context_impl_->host_impl()
+                                        ->active_tree()
+                                        ->property_trees()
+                                        ->effect_tree_mutable()
+                                        .TakeCopyRequests();
+  EXPECT_EQ(copy_requests_after_remove.count(cc::kSecondaryRootPropertyNodeId),
+            0u);
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeEffectNodeTest,
+       InvalidEffectNodeParentId) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::EffectNode::New();
+  node_update->id = next_effect_id_++;  // New node
+  node_update->parent_id = 99;          // Invalid parent ID
+  node_update->transform_id = cc::kRootPropertyNodeId;
+  node_update->clip_id = cc::kRootPropertyNodeId;
+  node_update->target_id = cc::kRootPropertyNodeId;
+  update->effect_nodes.push_back(std::move(node_update));
+  update->num_effect_nodes = next_effect_id_;
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid property tree node parent_id");
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeEffectNodeTest,
+       InvalidEffectNodeTransformId) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::EffectNode::New();
+  node_update->id = cc::kSecondaryRootPropertyNodeId;  // Existing node
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  node_update->transform_id = 99;  // Invalid transform ID
+  node_update->clip_id = cc::kRootPropertyNodeId;
+  node_update->target_id = cc::kRootPropertyNodeId;
+  update->effect_nodes.push_back(std::move(node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid transform_id for effect node");
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeEffectNodeTest,
+       InvalidEffectNodeClipId) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::EffectNode::New();
+  node_update->id = cc::kSecondaryRootPropertyNodeId;  // Existing node
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  node_update->transform_id = cc::kRootPropertyNodeId;
+  node_update->clip_id = 99;  // Invalid clip ID
+  node_update->target_id = cc::kRootPropertyNodeId;
+  update->effect_nodes.push_back(std::move(node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid clip_id for effect node");
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeEffectNodeTest,
+       InvalidEffectNodeTargetId) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::EffectNode::New();
+  node_update->id = cc::kSecondaryRootPropertyNodeId;  // Existing node
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  node_update->transform_id = cc::kRootPropertyNodeId;
+  node_update->clip_id = cc::kRootPropertyNodeId;
+  node_update->target_id = 99;  // Invalid target ID
+  update->effect_nodes.push_back(std::move(node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid target_id for effect node");
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeEffectNodeTest, InvalidBlendMode) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::EffectNode::New();
+  node_update->id = cc::kSecondaryRootPropertyNodeId;  // Existing node
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  node_update->transform_id = cc::kRootPropertyNodeId;
+  node_update->clip_id = cc::kRootPropertyNodeId;
+  node_update->target_id = cc::kRootPropertyNodeId;
+  node_update->blend_mode = 999;  // Invalid blend mode
+  update->effect_nodes.push_back(std::move(node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid blend_mode for effect node");
+}
+
+class LayerContextImplUpdateDisplayTreeScrollNodeTest
+    : public LayerContextImplTest {
+ protected:
+  cc::ScrollNode* GetScrollNodeFromActiveTree(int node_id) {
+    if (node_id < static_cast<int>(layer_context_impl_->host_impl()
+                                       ->active_tree()
+                                       ->property_trees()
+                                       ->scroll_tree()
+                                       .size())) {
+      return layer_context_impl_->host_impl()
+          ->active_tree()
+          ->property_trees()
+          ->scroll_tree_mutable()
+          .Node(node_id);
+    }
+    return nullptr;
+  }
+};
+
+TEST_F(LayerContextImplUpdateDisplayTreeScrollNodeTest,
+       UpdateExistingScrollNodeProperties) {
+  // Apply a default valid update first.
+  auto update1 = CreateDefaultUpdate();
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+
+  auto update2 = CreateDefaultUpdate();
+  auto node_update = mojom::ScrollNode::New();
+  node_update->id = cc::kSecondaryRootPropertyNodeId;
+  // Keep parent_id same as default.
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  node_update->container_bounds = gfx::Size(50, 60);
+  node_update->bounds = gfx::Size(70, 80);
+  node_update->user_scrollable_horizontal = true;
+  node_update->user_scrollable_vertical = true;
+  node_update->element_id = cc::ElementId(123);
+  // Use a valid existing transform node ID.
+  node_update->transform_id = cc::kSecondaryRootPropertyNodeId;
+  update2->scroll_nodes.push_back(std::move(node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update2));
+  ASSERT_TRUE(result.has_value());
+
+  cc::ScrollNode* node_impl =
+      GetScrollNodeFromActiveTree(cc::kSecondaryRootPropertyNodeId);
+  ASSERT_TRUE(node_impl);
+  EXPECT_EQ(node_impl->container_bounds, gfx::Size(50, 60));
+  EXPECT_EQ(node_impl->bounds, gfx::Size(70, 80));
+  EXPECT_TRUE(node_impl->user_scrollable_horizontal);
+  EXPECT_TRUE(node_impl->user_scrollable_vertical);
+  EXPECT_EQ(node_impl->element_id, cc::ElementId(123));
+  EXPECT_EQ(node_impl->transform_id, cc::kSecondaryRootPropertyNodeId);
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeScrollNodeTest, AddRemoveScrollNodes) {
+  // Apply a default valid update first.
+  auto update1 = CreateDefaultUpdate();
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+  uint32_t initial_node_count = layer_context_impl_->host_impl()
+                                    ->active_tree()
+                                    ->property_trees()
+                                    ->scroll_tree()
+                                    .nodes()
+                                    .size();
+
+  // Add a new node.
+  auto update_add = CreateDefaultUpdate();
+  int new_node_id =
+      AddScrollNode(update_add.get(), cc::kSecondaryRootPropertyNodeId);
+  EXPECT_EQ(update_add->num_scroll_nodes, initial_node_count + 1);
+
+  auto result_add =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_add));
+  ASSERT_TRUE(result_add.has_value());
+  EXPECT_EQ(layer_context_impl_->host_impl()
+                ->active_tree()
+                ->property_trees()
+                ->scroll_tree()
+                .nodes()
+                .size(),
+            initial_node_count + 1);
+  cc::ScrollNode* added_node_impl = GetScrollNodeFromActiveTree(new_node_id);
+  ASSERT_TRUE(added_node_impl);
+  EXPECT_EQ(added_node_impl->parent_id, cc::kSecondaryRootPropertyNodeId);
+
+  // Remove the added node.
+  auto update_remove = CreateDefaultUpdate();
+  update_remove->num_scroll_nodes = initial_node_count;
+  update_remove->scroll_nodes.clear();
+
+  auto result_remove =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_remove));
+  ASSERT_TRUE(result_remove.has_value());
+  EXPECT_EQ(layer_context_impl_->host_impl()
+                ->active_tree()
+                ->property_trees()
+                ->scroll_tree()
+                .nodes()
+                .size(),
+            initial_node_count);
+  EXPECT_FALSE(GetScrollNodeFromActiveTree(new_node_id));
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeScrollNodeTest,
+       InvalidScrollNodeParentId) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::ScrollNode::New();
+  node_update->id = next_scroll_id_++;  // New node
+  node_update->parent_id = 99;          // Invalid parent ID
+  node_update->transform_id = cc::kRootPropertyNodeId;
+  update->scroll_nodes.push_back(std::move(node_update));
+  update->num_scroll_nodes = next_scroll_id_;
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid property tree node parent_id");
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeScrollNodeTest,
+       InvalidScrollNodeTransformId) {
+  auto update = CreateDefaultUpdate();
+  auto node_update = mojom::ScrollNode::New();
+  node_update->id = cc::kSecondaryRootPropertyNodeId;  // Existing node
+  node_update->parent_id = cc::kRootPropertyNodeId;
+  node_update->transform_id = 99;  // Invalid transform ID
+  update->scroll_nodes.push_back(std::move(node_update));
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(), "Invalid transform_id for scroll node");
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeScrollNodeTest,
+       UpdateScrollTreeProperties) {
+  auto update = CreateDefaultUpdate();
+  auto tree_props = mojom::ScrollTreeUpdate::New();
+  cc::ElementId element_id(123);
+  tree_props->synced_scroll_offsets[element_id] =
+      base::MakeRefCounted<cc::SyncedScrollOffset>();
+  tree_props->synced_scroll_offsets[element_id]->SetCurrent(
+      gfx::PointF(10.f, 20.f));
+  tree_props->scrolling_contents_cull_rects[element_id] =
+      gfx::Rect(5, 5, 15, 15);
+  update->scroll_tree_update = std::move(tree_props);
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_TRUE(result.has_value());
+
+  const auto& scroll_tree = layer_context_impl_->host_impl()
+                                ->active_tree()
+                                ->property_trees()
+                                ->scroll_tree();
+  EXPECT_EQ(scroll_tree.synced_scroll_offset_map()
+                .at(element_id)
+                ->Current(
+                    /*is_active_tree=*/true),
+            gfx::PointF(10.f, 20.f));
+  EXPECT_EQ(scroll_tree.scrolling_contents_cull_rects().at(element_id),
+            gfx::Rect(5, 5, 15, 15));
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeScrollNodeTest,
+       EmptyScrollingContentsCullRectsByDefault) {
   EXPECT_TRUE(layer_context_impl_->host_impl()
                   ->active_tree()
                   ->property_trees()
@@ -275,32 +1103,6 @@
                   .empty());
 }
 
-TEST_F(LayerContextImplTest, ScrollingContentsCullRectsAreSynchronized) {
-  constexpr cc::ElementId kElementId = cc::ElementId(42);
-  constexpr gfx::Rect kCullRect = gfx::Rect{100, 100};
-  base::flat_map<cc::ElementId, gfx::Rect> scrolling_contents_cull_rects;
-  scrolling_contents_cull_rects[kElementId] = kCullRect;
-
-  auto scroll_tree_update = mojom::ScrollTreeUpdate::New();
-  scroll_tree_update->scrolling_contents_cull_rects =
-      scrolling_contents_cull_rects;
-
-  auto update = CreateDefaultUpdate();
-  update->scroll_tree_update = std::move(scroll_tree_update);
-
-  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
-  EXPECT_TRUE(result.has_value());
-
-  auto synchronized_scrolling_contents_cull_rects =
-      layer_context_impl_->host_impl()
-          ->active_tree()
-          ->property_trees()
-          ->scroll_tree()
-          .scrolling_contents_cull_rects();
-  EXPECT_EQ(scrolling_contents_cull_rects,
-            synchronized_scrolling_contents_cull_rects);
-}
-
 class LayerContextImplUpdateDisplayTreePageScaleFactorTest
     : public LayerContextImplTest,
       public ::testing::WithParamInterface<std::tuple<float, bool>> {};
@@ -815,7 +1617,8 @@
       const gfx::Size& bounds = gfx::Size(10, 10),
       int transform_idx = cc::kSecondaryRootPropertyNodeId,
       int clip_idx = cc::kRootPropertyNodeId,
-      int effect_idx = cc::kSecondaryRootPropertyNodeId) {
+      int effect_idx = cc::kSecondaryRootPropertyNodeId,
+      int scroll_idx = cc::kSecondaryRootPropertyNodeId) {
     auto layer = mojom::Layer::New();
     layer->id = id;
     layer->type = type;
@@ -823,6 +1626,7 @@
     layer->transform_tree_index = transform_idx;
     layer->clip_tree_index = clip_idx;
     layer->effect_tree_index = effect_idx;
+    layer->scroll_tree_index = scroll_idx;
     return layer;
   }
 };
@@ -1047,4 +1851,443 @@
   VerifyLayerOrder({1, kLayerId3, kLayerId1});
 }
 
+TEST_F(LayerContextImplLayerLifecycleTest, CreateLayersOfAllTypes) {
+  auto update = CreateDefaultUpdate();
+
+  // Test a subset of layer types that have distinct LayerImpl classes or
+  // specific handling in CreateLayer.
+  const std::vector<cc::mojom::LayerType> types_to_test = {
+      cc::mojom::LayerType::kLayer,
+      cc::mojom::LayerType::kMirror,
+      cc::mojom::LayerType::kNinePatchThumbScrollbar,
+      cc::mojom::LayerType::kPaintedScrollbar,
+      cc::mojom::LayerType::kTileDisplay,
+      cc::mojom::LayerType::kSolidColorScrollbar,
+      cc::mojom::LayerType::kSurface,
+      cc::mojom::LayerType::kTexture,
+      cc::mojom::LayerType::kViewTransitionContent,
+      // Add other relevant types here.
+  };
+
+  std::vector<int> layer_ids;
+  for (cc::mojom::LayerType type : types_to_test) {
+    int layer_id = next_layer_id_++;
+    layer_ids.push_back(layer_id);
+    auto layer = CreateManualLayer(layer_id, type);
+    switch (type) {
+      case cc::mojom::LayerType::kMirror: {
+        auto extra = mojom::MirrorLayerExtra::New();
+        // Mirroring the root layer (ID 1) by default for simplicity.
+        extra->mirrored_layer_id = 1;
+        layer->layer_extra =
+            mojom::LayerExtra::NewMirrorLayerExtra(std::move(extra));
+        break;
+      }
+      case cc::mojom::LayerType::kNinePatchThumbScrollbar: {
+        auto extra = mojom::NinePatchThumbScrollbarLayerExtra::New();
+        extra->scrollbar_base_extra = mojom::ScrollbarLayerBaseExtra::New();
+        layer->layer_extra =
+            mojom::LayerExtra::NewNinePatchThumbScrollbarLayerExtra(
+                std::move(extra));
+        break;
+      }
+      case cc::mojom::LayerType::kPaintedScrollbar: {
+        auto extra = mojom::PaintedScrollbarLayerExtra::New();
+        extra->scrollbar_base_extra = mojom::ScrollbarLayerBaseExtra::New();
+        layer->layer_extra =
+            mojom::LayerExtra::NewPaintedScrollbarLayerExtra(std::move(extra));
+        break;
+      }
+      case cc::mojom::LayerType::kSolidColorScrollbar: {
+        auto extra = mojom::SolidColorScrollbarLayerExtra::New();
+        extra->scrollbar_base_extra = mojom::ScrollbarLayerBaseExtra::New();
+        layer->layer_extra =
+            mojom::LayerExtra::NewSolidColorScrollbarLayerExtra(
+                std::move(extra));
+        break;
+      }
+      case cc::mojom::LayerType::kSurface: {
+        auto extra = mojom::SurfaceLayerExtra::New();
+        extra->surface_range = SurfaceRange(
+            std::nullopt,
+            SurfaceId(kDefaultFrameSinkId, default_local_surface_id_));
+        extra->deadline_in_frames = 0u;
+        layer->layer_extra =
+            mojom::LayerExtra::NewSurfaceLayerExtra(std::move(extra));
+        break;
+      }
+      case cc::mojom::LayerType::kTexture: {
+        auto extra = mojom::TextureLayerExtra::New();
+        // TextureLayer can have an optional TransferableResource.
+        // For this basic creation test, leaving it null is fine.
+        layer->layer_extra =
+            mojom::LayerExtra::NewTextureLayerExtra(std::move(extra));
+        break;
+      }
+      case cc::mojom::LayerType::kViewTransitionContent: {
+        auto extra = mojom::ViewTransitionContentLayerExtra::New();
+        extra->resource_id = ViewTransitionElementResourceId(
+            blink::ViewTransitionToken(), 1, false);
+        layer->layer_extra =
+            mojom::LayerExtra::NewViewTransitionContentLayerExtra(
+                std::move(extra));
+        break;
+      }
+      default:
+        // No layer_extra needed for other types in this test.
+        break;
+    }
+    update->layers.push_back(std::move(layer));
+    layer_order_.push_back(layer_id);
+  }
+  update->layer_order = layer_order_;
+
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value());
+
+  for (size_t i = 0; i < types_to_test.size(); ++i) {
+    VerifyLayerExists(layer_ids[i], true);
+    cc::LayerImpl* layer = GetLayerFromActiveTree(layer_ids[i]);
+    ASSERT_NE(nullptr, layer);
+    EXPECT_EQ(layer->GetLayerType(), types_to_test[i]);
+  }
+}
+
+TEST_F(LayerContextImplLayerLifecycleTest, UpdateMultipleLayerProperties) {
+  auto update = CreateDefaultUpdate();
+  int layer_id1 = AddDefaultLayerToUpdate(update.get());
+  int layer_id2 = AddDefaultLayerToUpdate(update.get());
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value());
+
+  auto update_props = CreateDefaultUpdate();
+  auto layer1_props = CreateManualLayer(layer_id1);
+  layer1_props->bounds = gfx::Size(50, 50);
+  layer1_props->contents_opaque = true;
+  layer1_props->contents_opaque_for_text = true;
+  layer1_props->background_color = SkColors::kRed;
+  layer1_props->transform_tree_index = cc::kRootPropertyNodeId;
+  update_props->layers.push_back(std::move(layer1_props));
+
+  auto layer2_props = CreateManualLayer(layer_id2);
+  layer2_props->is_drawable = false;
+  layer2_props->clip_tree_index = cc::kSecondaryRootPropertyNodeId;
+  layer2_props->effect_tree_index = cc::kRootPropertyNodeId;
+  update_props->layers.push_back(std::move(layer2_props));
+
+  EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_props))
+                  .has_value());
+
+  cc::LayerImpl* layer1_impl = GetLayerFromActiveTree(layer_id1);
+  ASSERT_NE(nullptr, layer1_impl);
+  EXPECT_EQ(layer1_impl->bounds(), gfx::Size(50, 50));
+  EXPECT_TRUE(layer1_impl->contents_opaque());
+  EXPECT_TRUE(layer1_impl->contents_opaque_for_text());
+  EXPECT_EQ(layer1_impl->background_color(), SkColors::kRed);
+  EXPECT_EQ(layer1_impl->transform_tree_index(), cc::kRootPropertyNodeId);
+
+  cc::LayerImpl* layer2_impl = GetLayerFromActiveTree(layer_id2);
+  ASSERT_NE(nullptr, layer2_impl);
+  EXPECT_FALSE(layer2_impl->draws_content());
+  EXPECT_EQ(layer2_impl->clip_tree_index(), cc::kSecondaryRootPropertyNodeId);
+  EXPECT_EQ(layer2_impl->effect_tree_index(), cc::kRootPropertyNodeId);
+}
+
+TEST_F(LayerContextImplLayerLifecycleTest, ReorderLayers) {
+  auto update = CreateDefaultUpdate();
+  int layer_id1 = AddDefaultLayerToUpdate(update.get());
+  int layer_id2 = AddDefaultLayerToUpdate(update.get());
+  int layer_id3 = AddDefaultLayerToUpdate(update.get());
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value());
+  VerifyLayerOrder({1, layer_id1, layer_id2, layer_id3});
+
+  // Move layer_id1 to the end.
+  auto update_reorder1 = CreateDefaultUpdate();
+  layer_order_ = {1, layer_id2, layer_id3, layer_id1};
+  update_reorder1->layer_order = layer_order_;
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_reorder1))
+          .has_value());
+  VerifyLayerOrder({1, layer_id2, layer_id3, layer_id1});
+
+  // Move layer_id3 to the beginning (after root).
+  auto update_reorder2 = CreateDefaultUpdate();
+  layer_order_ = {1, layer_id3, layer_id2, layer_id1};
+  update_reorder2->layer_order = layer_order_;
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_reorder2))
+          .has_value());
+  VerifyLayerOrder({1, layer_id3, layer_id2, layer_id1});
+}
+
+TEST_F(LayerContextImplLayerLifecycleTest, RemoveLayers) {
+  auto update = CreateDefaultUpdate();
+  int layer_id1 = AddDefaultLayerToUpdate(update.get());
+  int layer_id2 = AddDefaultLayerToUpdate(update.get());
+  int layer_id3 = AddDefaultLayerToUpdate(update.get());
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value());
+  VerifyLayerOrder({1, layer_id1, layer_id2, layer_id3});
+
+  // Remove from the middle.
+  auto update_remove1 = CreateDefaultUpdate();
+  RemoveLayerInUpdate(update_remove1.get(), layer_id2);
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_remove1))
+          .has_value());
+  VerifyLayerExists(layer_id2, false);
+  VerifyLayerOrder({1, layer_id1, layer_id3});
+
+  // Remove from the beginning (after root).
+  auto update_remove2 = CreateDefaultUpdate();
+  RemoveLayerInUpdate(update_remove2.get(), layer_id1);
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_remove2))
+          .has_value());
+  VerifyLayerExists(layer_id1, false);
+  VerifyLayerOrder({1, layer_id3});
+
+  // Remove from the end.
+  auto update_remove3 = CreateDefaultUpdate();
+  RemoveLayerInUpdate(update_remove3.get(), layer_id3);
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_remove3))
+          .has_value());
+  VerifyLayerExists(layer_id3, false);
+  VerifyLayerOrder({1});
+}
+
+TEST_F(LayerContextImplLayerLifecycleTest, LayerPropertyChangedFlags) {
+  auto update = CreateDefaultUpdate();
+  int layer_id = AddDefaultLayerToUpdate(update.get());
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value());
+
+  // Test layer_property_changed_not_from_property_trees
+  auto update_flag1 = CreateDefaultUpdate();
+  auto layer_props1 = CreateManualLayer(layer_id);
+  layer_props1->layer_property_changed_not_from_property_trees = true;
+  update_flag1->layers.push_back(std::move(layer_props1));
+  EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_flag1))
+                  .has_value());
+  cc::LayerImpl* layer_impl_flag1 = GetLayerFromActiveTree(layer_id);
+  ASSERT_NE(nullptr, layer_impl_flag1);
+  EXPECT_TRUE(layer_impl_flag1->LayerPropertyChangedNotFromPropertyTrees());
+
+  // Test layer_property_changed_from_property_trees
+  auto update_flag2 = CreateDefaultUpdate();
+  auto layer_props2 = CreateManualLayer(layer_id);
+  layer_props2->layer_property_changed_from_property_trees = true;
+  update_flag2->layers.push_back(std::move(layer_props2));
+  EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_flag2))
+                  .has_value());
+  // This flag is reset after processing, so we can't directly verify it here
+  // without more complex state tracking or inspecting internal LayerImpl
+  // states that are affected by it. For now, we ensure the update passes.
+  // A more thorough test would involve checking if draw properties were
+  // actually updated.
+  ASSERT_NE(nullptr, GetLayerFromActiveTree(layer_id));
+}
+
+TEST_F(LayerContextImplLayerLifecycleTest, RareProperties) {
+  auto update = CreateDefaultUpdate();
+  int layer_id = AddDefaultLayerToUpdate(update.get());
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value());
+
+  const auto kFirstId = RegionCaptureCropId::CreateRandom();
+  const auto kSecondId = RegionCaptureCropId::CreateRandom();
+  const RegionCaptureBounds kLayerBounds{
+      {{kFirstId, gfx::Rect{0, 0, 250, 250}}, {kSecondId, gfx::Rect{}}}};
+
+  auto update_rare = CreateDefaultUpdate();
+  auto layer_props = CreateManualLayer(layer_id);
+  layer_props->rare_properties = mojom::RareProperties::New();
+  layer_props->rare_properties->filter_quality =
+      cc::PaintFlags::FilterQuality::kMedium;
+  layer_props->rare_properties->dynamic_range_limit =
+      cc::PaintFlags::DynamicRangeLimitMixture(1.f, 0.5f);
+  layer_props->rare_properties->capture_bounds = kLayerBounds;
+  update_rare->layers.push_back(std::move(layer_props));
+
+  EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_rare))
+                  .has_value());
+
+  cc::LayerImpl* layer_impl_rare = GetLayerFromActiveTree(layer_id);
+  ASSERT_NE(nullptr, layer_impl_rare);
+  EXPECT_EQ(layer_impl_rare->GetFilterQuality(),
+            cc::PaintFlags::FilterQuality::kMedium);
+  EXPECT_EQ(layer_impl_rare->GetDynamicRangeLimit(),
+            cc::PaintFlags::DynamicRangeLimitMixture(1.f, 0.5f));
+  ASSERT_TRUE(layer_impl_rare->capture_bounds());
+  EXPECT_EQ(*layer_impl_rare->capture_bounds(), kLayerBounds);
+}
+
+TEST_F(LayerContextImplLayerLifecycleTest, ContentsOpaqueFlags) {
+  ResetTestState();
+  auto update = CreateDefaultUpdate();
+  int layer_id = AddDefaultLayerToUpdate(update.get());
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value());
+
+  // Valid: contents_opaque = true, contents_opaque_for_text = true
+  auto update_valid1 = CreateDefaultUpdate();
+  auto layer_props_valid1 = CreateManualLayer(layer_id);
+  layer_props_valid1->contents_opaque = true;
+  layer_props_valid1->contents_opaque_for_text = true;
+  update_valid1->layers.push_back(std::move(layer_props_valid1));
+  EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_valid1))
+                  .has_value());
+  cc::LayerImpl* layer_impl_valid1 = GetLayerFromActiveTree(layer_id);
+  ASSERT_NE(nullptr, layer_impl_valid1);
+  EXPECT_TRUE(layer_impl_valid1->contents_opaque());
+  EXPECT_TRUE(layer_impl_valid1->contents_opaque_for_text());
+
+  // Invalid: contents_opaque = true, contents_opaque_for_text = false
+  auto update_invalid = CreateDefaultUpdate();
+  auto layer_props_invalid = CreateManualLayer(layer_id);
+  layer_props_invalid->contents_opaque = true;
+  layer_props_invalid->contents_opaque_for_text = false;
+  update_invalid->layers.push_back(std::move(layer_props_invalid));
+  auto result_invalid =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_invalid));
+  ASSERT_FALSE(result_invalid.has_value());
+  EXPECT_EQ(result_invalid.error(),
+            "Invalid contents_opaque_for_text: cannot be false if "
+            "contents_opaque is true.");
+  // Verify properties remain from the last valid update
+  cc::LayerImpl* layer_impl_invalid = GetLayerFromActiveTree(layer_id);
+  ASSERT_NE(nullptr, layer_impl_invalid);
+  EXPECT_TRUE(layer_impl_invalid->contents_opaque());
+  EXPECT_TRUE(layer_impl_invalid->contents_opaque_for_text());
+
+  // Valid: contents_opaque = false, contents_opaque_for_text = true
+  auto update_valid2 = CreateDefaultUpdate();
+  auto layer_props_valid2 = CreateManualLayer(layer_id);
+  layer_props_valid2->contents_opaque = false;
+  layer_props_valid2->contents_opaque_for_text = true;
+  update_valid2->layers.push_back(std::move(layer_props_valid2));
+  EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_valid2))
+                  .has_value());
+  cc::LayerImpl* layer_impl_valid2 = GetLayerFromActiveTree(layer_id);
+  ASSERT_NE(nullptr, layer_impl_valid2);
+  EXPECT_FALSE(layer_impl_valid2->contents_opaque());
+  EXPECT_TRUE(layer_impl_valid2->contents_opaque_for_text());
+
+  // Valid: contents_opaque = false, contents_opaque_for_text = false
+  auto update_valid3 = CreateDefaultUpdate();
+  auto layer_props_valid3 = CreateManualLayer(layer_id);
+  layer_props_valid3->contents_opaque = false;
+  layer_props_valid3->contents_opaque_for_text = false;
+  update_valid3->layers.push_back(std::move(layer_props_valid3));
+  EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_valid3))
+                  .has_value());
+  cc::LayerImpl* layer_impl_valid3 = GetLayerFromActiveTree(layer_id);
+  ASSERT_NE(nullptr, layer_impl_valid3);
+  EXPECT_FALSE(layer_impl_valid3->contents_opaque());
+  EXPECT_FALSE(layer_impl_valid3->contents_opaque_for_text());
+}
+
+TEST_F(LayerContextImplLayerLifecycleTest, MissingLayerExtra) {
+  const std::vector<cc::mojom::LayerType> types_requiring_extra = {
+      cc::mojom::LayerType::kMirror,
+      cc::mojom::LayerType::kNinePatchThumbScrollbar,
+      cc::mojom::LayerType::kPaintedScrollbar,
+      cc::mojom::LayerType::kSolidColorScrollbar,
+      cc::mojom::LayerType::kSurface,
+      cc::mojom::LayerType::kTexture,
+      cc::mojom::LayerType::kViewTransitionContent,
+  };
+
+  for (cc::mojom::LayerType type : types_requiring_extra) {
+    SCOPED_TRACE(testing::Message()
+                 << "Testing LayerType: " << static_cast<int>(type));
+    ResetTestState();
+    // Create a valid root layer first.
+    auto initial_update = CreateDefaultUpdate();
+    EXPECT_TRUE(
+        layer_context_impl_->DoUpdateDisplayTree(std::move(initial_update))
+            .has_value());
+
+    auto update_missing_extra = CreateDefaultUpdate();
+    int layer_id = next_layer_id_++;
+    // Create the layer manually without setting layer_extra.
+    auto layer = CreateManualLayer(layer_id, type);
+    // Ensure layer_extra is indeed null.
+    layer->layer_extra = nullptr;
+
+    update_missing_extra->layers.push_back(std::move(layer));
+    update_missing_extra->layer_order = layer_order_;
+    update_missing_extra->layer_order->push_back(layer_id);
+
+    auto result = layer_context_impl_->DoUpdateDisplayTree(
+        std::move(update_missing_extra));
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error(), "Invalid layer_extra");
+  }
+}
+
+TEST_F(LayerContextImplLayerLifecycleTest,
+       UpdateExistingLayerWithInvalidPropertyTreeIndicesFails) {
+  constexpr int kLayerId = 2;
+  constexpr int kValidIndex = 1;     // Assumes root (0) and secondary_root (1)
+  constexpr int kInvalidIndex = 99;  // An index that will be out of bounds.
+
+  // Setup: Create a layer with valid indices and small property trees.
+  auto setup_update = CreateDefaultUpdate();
+
+  AddDefaultLayerToUpdate(setup_update.get(), cc::mojom::LayerType::kLayer,
+                          kLayerId);
+  // Set initial valid indices for the layer.
+  setup_update->layers.back()->transform_tree_index = kValidIndex;
+  setup_update->layers.back()->clip_tree_index = kValidIndex;
+  setup_update->layers.back()->effect_tree_index = kValidIndex;
+  setup_update->layers.back()->scroll_tree_index = kValidIndex;
+
+  EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(setup_update))
+                  .has_value());
+  VerifyLayerExists(kLayerId, true);
+
+  // Test Case 1: Update with invalid transform_tree_index.
+  auto update_invalid_transform = CreateDefaultUpdate();
+  update_invalid_transform->layers.push_back(CreateManualLayer(
+      kLayerId, cc::mojom::LayerType::kLayer, gfx::Size(10, 10), kInvalidIndex,
+      kValidIndex, kValidIndex, kValidIndex));
+  auto result_transform = layer_context_impl_->DoUpdateDisplayTree(
+      std::move(update_invalid_transform));
+  ASSERT_FALSE(result_transform.has_value());
+  EXPECT_THAT(result_transform.error(),
+              testing::StartsWith("Invalid transform tree ID"));
+
+  // Test Case 2: Update with invalid clip_tree_index.
+  auto update_invalid_clip = CreateDefaultUpdate();
+  update_invalid_clip->layers.push_back(CreateManualLayer(
+      kLayerId, cc::mojom::LayerType::kLayer, gfx::Size(10, 10), kValidIndex,
+      kInvalidIndex, kValidIndex, kValidIndex));
+  auto result_clip =
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update_invalid_clip));
+  ASSERT_FALSE(result_clip.has_value());
+  EXPECT_THAT(result_clip.error(), testing::StartsWith("Invalid clip tree ID"));
+
+  // Test Case 3: Update with invalid effect_tree_index (similar for scroll).
+  auto update_invalid_effect = CreateDefaultUpdate();
+  update_invalid_effect->layers.push_back(CreateManualLayer(
+      kLayerId, cc::mojom::LayerType::kLayer, gfx::Size(10, 10), kValidIndex,
+      kValidIndex, kInvalidIndex, kValidIndex));
+  auto result_effect = layer_context_impl_->DoUpdateDisplayTree(
+      std::move(update_invalid_effect));
+  ASSERT_FALSE(result_effect.has_value());
+  EXPECT_THAT(result_effect.error(),
+              testing::StartsWith("Invalid effect tree ID"));
+
+  // Verify layer properties remain from the last successful update.
+  cc::LayerImpl* layer_impl_after_invalid = GetLayerFromActiveTree(kLayerId);
+  ASSERT_NE(nullptr, layer_impl_after_invalid);
+  EXPECT_EQ(layer_impl_after_invalid->transform_tree_index(), kValidIndex);
+  EXPECT_EQ(layer_impl_after_invalid->clip_tree_index(), kValidIndex);
+  EXPECT_EQ(layer_impl_after_invalid->effect_tree_index(), kValidIndex);
+  EXPECT_EQ(layer_impl_after_invalid->scroll_tree_index(), kValidIndex);
+}
+
 }  // namespace viz
diff --git a/components/viz/service/main/viz_main_impl.cc b/components/viz/service/main/viz_main_impl.cc
index d79bdcff..379e069 100644
--- a/components/viz/service/main/viz_main_impl.cc
+++ b/components/viz/service/main/viz_main_impl.cc
@@ -158,6 +158,10 @@
   if (dependencies_.ukm_recorder)
     ukm::DelegatingUkmRecorder::Get()->RemoveDelegate(
         dependencies_.ukm_recorder.get());
+
+  if (!gpu_init_->gpu_info().in_process_gpu) {
+    GpuLogMessageManager::GetInstance()->ShutdownLogging();
+  }
 }
 
 void VizMainImpl::Bind(mojo::PendingReceiver<mojom::VizMain> receiver) {
@@ -167,6 +171,7 @@
 void VizMainImpl::CreateGpuService(
     mojo::PendingReceiver<mojom::GpuService> pending_receiver,
     mojo::PendingRemote<mojom::GpuHost> pending_gpu_host,
+    mojo::PendingRemote<mojom::GpuLogging> pending_gpu_logging,
     mojo::PendingRemote<
         discardable_memory::mojom::DiscardableSharedMemoryManager>
         discardable_memory_manager,
@@ -182,7 +187,9 @@
 
   if (!gpu_init_->init_successful()) {
     LOG(ERROR) << "Exiting GPU process due to errors during initialization";
-    GpuLogMessageManager::GetInstance()->FlushMessages(gpu_host.get());
+    mojo::Remote<mojom::GpuLogging> gpu_logging(std::move(pending_gpu_logging));
+    GpuLogMessageManager::GetInstance()->FlushMessages(gpu_logging.get());
+
     gpu_service_.reset();
     gpu_host->DidFailInitialize();
     if (delegate_)
@@ -198,6 +205,10 @@
         std::move(discardable_memory_manager), io_task_runner());
     base::DiscardableMemoryAllocator::SetInstance(
         discardable_shared_memory_manager_.get());
+
+    // Setup GPU Log message hook and bind the GPU logging interface.
+    GpuLogMessageManager::GetInstance()->InstallPostInitializeLogHandler(
+        std::move(pending_gpu_logging), io_task_runner());
   }
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/components/viz/service/main/viz_main_impl.h b/components/viz/service/main/viz_main_impl.h
index 84b3eb3..1d3bdb60 100644
--- a/components/viz/service/main/viz_main_impl.h
+++ b/components/viz/service/main/viz_main_impl.h
@@ -15,6 +15,7 @@
 #include "build/build_config.h"
 #include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
 #include "components/viz/common/buildflags.h"
+#include "components/viz/service/gl/gpu_log_message_manager.h"
 #include "components/viz/service/gl/gpu_service_impl.h"
 #include "components/viz/service/main/viz_compositor_thread_runner_impl.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -123,6 +124,7 @@
   void CreateGpuService(
       mojo::PendingReceiver<mojom::GpuService> pending_receiver,
       mojo::PendingRemote<mojom::GpuHost> pending_gpu_host,
+      mojo::PendingRemote<mojom::GpuLogging> pending_gpu_loggging,
       mojo::PendingRemote<
           discardable_memory::mojom::DiscardableSharedMemoryManager>
           discardable_memory_manager,
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 575f837..c7adbd86 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1921,6 +1921,8 @@
     "renderer_host/navigation_request_info.h",
     "renderer_host/navigation_state_keep_alive.cc",
     "renderer_host/navigation_state_keep_alive.h",
+    "renderer_host/navigation_throttle_registry_impl.cc",
+    "renderer_host/navigation_throttle_registry_impl.h",
     "renderer_host/navigation_throttle_runner.cc",
     "renderer_host/navigation_throttle_runner.h",
     "renderer_host/navigation_transitions/navigation_entry_screenshot.cc",
diff --git a/content/browser/background_fetch/background_fetch_test_base.cc b/content/browser/background_fetch/background_fetch_test_base.cc
index 2843ddb..e197c7c 100644
--- a/content/browser/background_fetch/background_fetch_test_base.cc
+++ b/content/browser/background_fetch/background_fetch_test_base.cc
@@ -138,7 +138,7 @@
 
   {
     base::RunLoop run_loop;
-    embedded_worker_test_helper_.context()->registry()->FindRegistrationForId(
+    embedded_worker_test_helper_.context()->registry().FindRegistrationForId(
         service_worker_registration_id, key,
         base::BindOnce(&DidFindServiceWorkerRegistration,
                        &service_worker_registration, run_loop.QuitClosure()));
diff --git a/content/browser/content_index/content_index_database_unittest.cc b/content/browser/content_index/content_index_database_unittest.cc
index 896d0ed..343936b 100644
--- a/content/browser/content_index/content_index_database_unittest.cc
+++ b/content/browser/content_index/content_index_database_unittest.cc
@@ -270,7 +270,7 @@
 
     {
       base::RunLoop run_loop;
-      embedded_worker_test_helper_.context()->registry()->FindRegistrationForId(
+      embedded_worker_test_helper_.context()->registry().FindRegistrationForId(
           service_worker_registration_id,
           blink::StorageKey::CreateFirstParty(origin),
           base::BindOnce(&DidFindServiceWorkerRegistration,
diff --git a/content/browser/devtools/devtools_background_services_context_impl_unittest.cc b/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
index c269cb1..e33db4db 100644
--- a/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
+++ b/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
@@ -125,7 +125,7 @@
   mojo::Remote<storage::mojom::ServiceWorkerStorageControl>& storage_control() {
     return embedded_worker_test_helper_.context()
         ->registry()
-        ->GetRemoteStorageControl();
+        .GetRemoteStorageControl();
   }
 
  protected:
@@ -247,7 +247,7 @@
 
     {
       base::RunLoop run_loop;
-      embedded_worker_test_helper_.context()->registry()->FindRegistrationForId(
+      embedded_worker_test_helper_.context()->registry().FindRegistrationForId(
           service_worker_registration_id, key,
           base::BindOnce(&DidFindServiceWorkerRegistration,
                          &service_worker_registration_,
diff --git a/content/browser/file_system_access/file_path_watcher/file_path_watcher_inotify.cc b/content/browser/file_system_access/file_path_watcher/file_path_watcher_inotify.cc
index d78eecc..5616fe18 100644
--- a/content/browser/file_system_access/file_path_watcher/file_path_watcher_inotify.cc
+++ b/content/browser/file_system_access/file_path_watcher/file_path_watcher_inotify.cc
@@ -42,7 +42,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/expected.h"
 #include "build/build_config.h"
 #include "content/browser/file_system_access/features.h"
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 7f4c54c7..e6ffa2b 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -812,7 +812,8 @@
         message += "was killed by you! Why?";
         break;
       case base::TERMINATION_STATUS_PROCESS_CRASHED:
-        message += "crashed!";
+        message +=
+            base::StringPrintf("crashed! Exit code: %d.", info.exit_code);
         unexpected_exit = true;
         break;
       case base::TERMINATION_STATUS_STILL_RUNNING:
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 44524240..bd3149d 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -39,8 +39,7 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
-#include "base/trace_event/common/trace_event_common.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/expected.h"
 #include "base/types/strong_alias.h"
 #include "build/build_config.h"
diff --git a/content/browser/indexed_db/instance/connection.cc b/content/browser/indexed_db/instance/connection.cc
index 1630fc23..e763b8c 100644
--- a/content/browser/indexed_db/instance/connection.cc
+++ b/content/browser/indexed_db/instance/connection.cc
@@ -24,7 +24,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/cxx23_to_underlying.h"
 #include "base/unguessable_token.h"
 #include "components/services/storage/indexed_db/locks/partitioned_lock_id.h"
diff --git a/content/browser/indexed_db/instance/cursor.cc b/content/browser/indexed_db/instance/cursor.cc
index f582a474..9d1cae24 100644
--- a/content/browser/indexed_db/instance/cursor.cc
+++ b/content/browser/indexed_db/instance/cursor.cc
@@ -16,7 +16,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "content/browser/indexed_db/indexed_db_database_error.h"
 #include "content/browser/indexed_db/indexed_db_external_object.h"
 #include "content/browser/indexed_db/indexed_db_value.h"
diff --git a/content/browser/indexed_db/instance/database.cc b/content/browser/indexed_db/instance/database.cc
index b98c82d..880a1878 100644
--- a/content/browser/indexed_db/instance/database.cc
+++ b/content/browser/indexed_db/instance/database.cc
@@ -29,7 +29,7 @@
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/expected_macros.h"
 #include "base/unguessable_token.h"
 #include "components/services/storage/indexed_db/locks/partitioned_lock_id.h"
diff --git a/content/browser/indexed_db/instance/leveldb/backing_store.cc b/content/browser/indexed_db/instance/leveldb/backing_store.cc
index 43d0238..6fb1222 100644
--- a/content/browser/indexed_db/instance/leveldb/backing_store.cc
+++ b/content/browser/indexed_db/instance/leveldb/backing_store.cc
@@ -43,8 +43,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/expected_macros.h"
 #include "build/build_config.h"
 #include "components/services/storage/indexed_db/locks/partitioned_lock.h"
diff --git a/content/browser/indexed_db/instance/leveldb/compaction_task.cc b/content/browser/indexed_db/instance/leveldb/compaction_task.cc
index 8674474..73f69fa 100644
--- a/content/browser/indexed_db/instance/leveldb/compaction_task.cc
+++ b/content/browser/indexed_db/instance/leveldb/compaction_task.cc
@@ -4,7 +4,7 @@
 
 #include "content/browser/indexed_db/instance/leveldb/compaction_task.h"
 
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 
 namespace content::indexed_db::level_db {
diff --git a/content/browser/indexed_db/instance/leveldb/tombstone_sweeper_unittest.cc b/content/browser/indexed_db/instance/leveldb/tombstone_sweeper_unittest.cc
index 2b17c24b..374d8bf3 100644
--- a/content/browser/indexed_db/instance/leveldb/tombstone_sweeper_unittest.cc
+++ b/content/browser/indexed_db/instance/leveldb/tombstone_sweeper_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/files/scoped_temp_dir.h"
+#include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/task_environment.h"
 #include "components/services/storage/indexed_db/leveldb/mock_level_db.h"
diff --git a/content/browser/indexed_db/instance/transaction.cc b/content/browser/indexed_db/instance/transaction.cc
index f5f6fc6..68ce8e2 100644
--- a/content/browser/indexed_db/instance/transaction.cc
+++ b/content/browser/indexed_db/instance/transaction.cc
@@ -26,7 +26,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/expected_macros.h"
 #include "base/unguessable_token.h"
 #include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h"
diff --git a/content/browser/indexed_db/instance/transaction_unittest.cc b/content/browser/indexed_db/instance/transaction_unittest.cc
index 9ac590a6..1639ddfc 100644
--- a/content/browser/indexed_db/instance/transaction_unittest.cc
+++ b/content/browser/indexed_db/instance/transaction_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/updateable_sequenced_task_runner.h"
 #include "base/test/run_until.h"
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc
index dd3834b..8ff5508ff 100644
--- a/content/browser/media/session/media_session_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/histogram_samples.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
diff --git a/content/browser/network_sandbox.cc b/content/browser/network_sandbox.cc
index 223ae083..32941720 100644
--- a/content/browser/network_sandbox.cc
+++ b/content/browser/network_sandbox.cc
@@ -11,7 +11,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
 #include "base/task/thread_pool.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "content/browser/network_sandbox_grant_result.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/content/browser/notifications/blink_notification_service_impl_unittest.cc b/content/browser/notifications/blink_notification_service_impl_unittest.cc
index 49a60e0a..6909b86e 100644
--- a/content/browser/notifications/blink_notification_service_impl_unittest.cc
+++ b/content/browser/notifications/blink_notification_service_impl_unittest.cc
@@ -187,7 +187,7 @@
 
     {
       base::RunLoop run_loop;
-      embedded_worker_helper_->context()->registry()->FindRegistrationForId(
+      embedded_worker_helper_->context()->registry().FindRegistrationForId(
           service_worker_registration_id, storage_key_,
           base::BindOnce(&BlinkNotificationServiceImplTest::
                              DidFindServiceWorkerRegistration,
diff --git a/content/browser/notifications/notification_storage_unittest.cc b/content/browser/notifications/notification_storage_unittest.cc
index 32b7dbf..a4ae524 100644
--- a/content/browser/notifications/notification_storage_unittest.cc
+++ b/content/browser/notifications/notification_storage_unittest.cc
@@ -90,7 +90,7 @@
 
     {
       base::RunLoop run_loop;
-      helper_->context()->registry()->FindRegistrationForId(
+      helper_->context()->registry().FindRegistrationForId(
           service_worker_registration_id_, key,
           base::BindOnce(
               &NotificationStorageTest::DidFindServiceWorkerRegistration,
diff --git a/content/browser/preloading/preloading_decider.cc b/content/browser/preloading/preloading_decider.cc
index 79ea857f..794f5b78 100644
--- a/content/browser/preloading/preloading_decider.cc
+++ b/content/browser/preloading/preloading_decider.cc
@@ -99,17 +99,12 @@
             blink::features::kPreloadingModelPrerenderModerateThreshold.Get(),
             0,
             100)} {
-    static const base::FeatureParam<std::string> kPointerDownEagerness{
-        &blink::features::kSpeculationRulesPointerDownHeuristics,
-        "pointer_down_eagerness", "conservative,moderate"};
     pointer_down_eagerness_ =
-        EagernessSetFromFeatureParam(kPointerDownEagerness.Get());
+        EagernessSet{blink::mojom::SpeculationEagerness::kConservative,
+                     blink::mojom::SpeculationEagerness::kModerate};
 
-    static const base::FeatureParam<std::string> kPointerHoverEagerness{
-        &blink::features::kSpeculationRulesPointerHoverHeuristics,
-        "pointer_hover_eagerness", "moderate"};
     pointer_hover_eagerness_ =
-        EagernessSetFromFeatureParam(kPointerHoverEagerness.Get());
+        EagernessSet{blink::mojom::SpeculationEagerness::kModerate};
 
     static const base::FeatureParam<std::string> kViewportHeuristicEagerness{
         &blink::features::kPreloadingViewportHeuristics,
diff --git a/content/browser/preloading/preloading_decider_unittest.cc b/content/browser/preloading/preloading_decider_unittest.cc
index 1ba56c2..91a5abc 100644
--- a/content/browser/preloading/preloading_decider_unittest.cc
+++ b/content/browser/preloading/preloading_decider_unittest.cc
@@ -500,62 +500,6 @@
         testing::Values(blink::mojom::SpeculationEagerness::kModerate,
                         blink::mojom::SpeculationEagerness::kConservative)));
 
-TEST_F(PreloadingDeciderTest, CanOverridePointerDownEagerness) {
-  // PreloadingDecider defaults to allowing it for conservative candidates,
-  // but for this test we'll allow it only for moderate.
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeatureWithParameters(
-      blink::features::kSpeculationRulesPointerDownHeuristics,
-      {{"pointer_down_eagerness", "moderate"}});
-
-  MockContentBrowserClient browser_client;
-  auto* preloading_decider =
-      PreloadingDecider::GetOrCreateForCurrentDocument(&GetPrimaryMainFrame());
-  ASSERT_TRUE(preloading_decider);
-
-  auto candidate =
-      MakeCandidate(GetSameOriginUrl("/candidate1.html"),
-                    blink::mojom::SpeculationAction::kPrefetch,
-                    blink::mojom::SpeculationEagerness::kConservative);
-  std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
-  candidates.push_back(std::move(candidate));
-
-  preloading_decider->UpdateSpeculationCandidates(candidates);
-  EXPECT_EQ(0u, GetPrefetchService()->prefetches_.size());
-
-  preloading_decider->OnPointerDown(GetSameOriginUrl("/candidate1.html"));
-  EXPECT_EQ(0u, GetPrefetchService()->prefetches_.size());
-}
-
-TEST_F(PreloadingDeciderTest, CanOverridePointerHoverEagerness) {
-  // PreloadingDecider defaults to allowing it for moderate candidates,
-  // but for this test we'll allow it only for conservative candidates too.
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeatureWithParameters(
-      blink::features::kSpeculationRulesPointerHoverHeuristics,
-      {{"pointer_hover_eagerness", "moderate,conservative"}});
-
-  MockContentBrowserClient browser_client;
-  auto* preloading_decider =
-      PreloadingDecider::GetOrCreateForCurrentDocument(&GetPrimaryMainFrame());
-  ASSERT_TRUE(preloading_decider);
-
-  auto candidate =
-      MakeCandidate(GetSameOriginUrl("/candidate1.html"),
-                    blink::mojom::SpeculationAction::kPrefetch,
-                    blink::mojom::SpeculationEagerness::kConservative);
-  std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
-  candidates.push_back(std::move(candidate));
-
-  preloading_decider->UpdateSpeculationCandidates(candidates);
-  EXPECT_EQ(0u, GetPrefetchService()->prefetches_.size());
-
-  preloading_decider->OnPointerHover(
-      GetSameOriginUrl("/candidate1.html"),
-      blink::mojom::AnchorElementPointerData::New(false, 0.0, 0.0));
-  EXPECT_EQ(1u, GetPrefetchService()->prefetches_.size());
-}
-
 TEST_F(PreloadingDeciderTest, UmaRecallStats) {
   base::HistogramTester histogram_tester;
   auto* preloading_decider =
diff --git a/content/browser/renderer_host/cookie_browsertest.cc b/content/browser/renderer_host/cookie_browsertest.cc
index f121b35..8ccad04 100644
--- a/content/browser/renderer_host/cookie_browsertest.cc
+++ b/content/browser/renderer_host/cookie_browsertest.cc
@@ -198,7 +198,7 @@
       static_cast<WebContentsImpl*>(shell2->web_contents());
   WebContentsImpl* web_contents_http =
       static_cast<WebContentsImpl*>(shell()->web_contents());
-  if (AreAllSitesIsolatedForTesting()) {
+  if (AreStrictSiteInstancesEnabled()) {
     EXPECT_EQ("http://a.test/",
               web_contents_http->GetSiteInstance()->GetSiteURL().spec());
     // Create expected site url, including port if origin isolation is enabled.
diff --git a/content/browser/renderer_host/ipc_utils.h b/content/browser/renderer_host/ipc_utils.h
index 254d2a74..e5591c9 100644
--- a/content/browser/renderer_host/ipc_utils.h
+++ b/content/browser/renderer_host/ipc_utils.h
@@ -7,6 +7,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "content/common/frame.mojom.h"
+#include "content/public/browser/render_process_host.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/blink/public/mojom/frame/frame.mojom.h"
 #include "third_party/blink/public/mojom/navigation/navigation_params.mojom-forward.h"
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index 36651b27..3d91e2c8 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -22976,12 +22976,12 @@
   EXPECT_EQ(origin_to_commit.value(), committed_origin);
 
   GURL site_url = contents()->GetSiteInstance()->GetSiteURL();
-  if (AreAllSitesIsolatedForTesting()) {
+  if (AreStrictSiteInstancesEnabled()) {
     EXPECT_EQ(site_url.spec(),
               "data:" + origin_to_commit->GetNonceForTesting()->ToString());
   } else {
-    // Without site isolation, the data: URL ends up in the default
-    // SiteInstance.
+    // Without site isolation and without DefaultSiteInstanceGroups, the data:
+    // URL ends up in the default SiteInstance.
     EXPECT_EQ(site_url.spec(), "http://unisolated.invalid/");
   }
 }
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 7567b56a7..2026d29 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -38,7 +38,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "base/types/optional_util.h"
 #include "base/types/pass_key.h"
 #include "build/build_config.h"
@@ -3178,8 +3178,9 @@
   modified_request_headers_.Clear();
   removed_request_headers_.clear();
 
+  throttle_registry_ = std::make_unique<NavigationThrottleRegistryImpl>(this);
   throttle_runner_ = std::make_unique<NavigationThrottleRunner>(
-      this, navigation_id_, IsInPrimaryMainFrame());
+      throttle_registry_.get(), navigation_id_, IsInPrimaryMainFrame());
 
   // For prerendered page activation, CommitDeferringConditions have already run
   // at the beginning of the navigation, so we won't run them again.
@@ -3258,6 +3259,11 @@
     }
   }
 
+  // Reset `throttle_runner_` and `throttle_registry_` in the reversed order of
+  // their creation as `throttle_runner_` has a pointer to `throttle_registry_`.
+  throttle_runner_.reset();
+  throttle_registry_.reset();
+
   // Reset the state of the NavigationRequest, and the navigation_handle_id.
   StopCommitTimeout();
   SetState(NOT_STARTED);
@@ -5374,9 +5380,8 @@
 #if DCHECK_IS_ON()
     if (result.action() == NavigationThrottle::BLOCK_REQUEST) {
       DCHECK(net::IsRequestBlockedError(result.net_error_code()));
-    }
-    // TODO(clamy): distinguish between CANCEL and CANCEL_AND_IGNORE.
-    else if (result.action() == NavigationThrottle::CANCEL_AND_IGNORE) {
+    } else if (result.action() == NavigationThrottle::CANCEL_AND_IGNORE) {
+      // TODO(clamy): distinguish between CANCEL and CANCEL_AND_IGNORE.
       DCHECK_EQ(result.net_error_code(), net::ERR_ABORTED);
     }
 #endif
@@ -5667,7 +5672,6 @@
 }
 
 void NavigationRequest::MaybeAddResourceTimingEntryForCancelledNavigation() {
-
   // Some navigation are cancelled even before requesting and receiving a
   // response. Those cases are not supported and the ResourceTiming is not
   // reported to the parent.
@@ -6121,7 +6125,7 @@
     // DO NOT ADD CODE after this. The previous call to CommitNavigation
     // destroyed the NavigationRequest.
     return;
-  };
+  }
 
   RunCommitDeferringConditions();
   // DO NOT ADD CODE after this. The previous call to
@@ -7698,26 +7702,26 @@
 }
 
 void NavigationRequest::OnNavigationEventProcessed(
-    NavigationThrottleRunner::Event event,
+    NavigationThrottleEvent event,
     NavigationThrottle::ThrottleCheckResult result) {
   DCHECK_NE(NavigationThrottle::DEFER, result.action());
   switch (event) {
-    case NavigationThrottleRunner::Event::kNoEvent:
+    case NavigationThrottleEvent::kNoEvent:
       DUMP_WILL_BE_NOTREACHED();
       return;
-    case NavigationThrottleRunner::Event::kWillStartRequest:
+    case NavigationThrottleEvent::kWillStartRequest:
       OnWillStartRequestProcessed(result);
       return;
-    case NavigationThrottleRunner::Event::kWillRedirectRequest:
+    case NavigationThrottleEvent::kWillRedirectRequest:
       OnWillRedirectRequestProcessed(result);
       return;
-    case NavigationThrottleRunner::Event::kWillFailRequest:
+    case NavigationThrottleEvent::kWillFailRequest:
       OnWillFailRequestProcessed(result);
       return;
-    case NavigationThrottleRunner::Event::kWillProcessResponse:
+    case NavigationThrottleEvent::kWillProcessResponse:
       OnWillProcessResponseProcessed(result);
       return;
-    case NavigationThrottleRunner::Event::kWillCommitWithoutUrlLoader:
+    case NavigationThrottleEvent::kWillCommitWithoutUrlLoader:
       OnWillCommitWithoutUrlLoaderProcessed(result);
       return;
   }
@@ -7906,7 +7910,7 @@
   DCHECK(!IsPageActivation())
       << "Attempted to register a NavigationThrottle for an activating "
          "navigation which will not work.";
-  throttle_runner_->AddThrottle(std::move(navigation_throttle));
+  throttle_registry_->AddThrottle(std::move(navigation_throttle));
 }
 bool NavigationRequest::IsDeferredForTesting() {
   return IsDeferred();
@@ -7997,7 +8001,7 @@
   // won't run them again on activation.
   if (!IsPageActivation()) {
     base::ElapsedTimer duration;
-    throttle_runner_->RegisterNavigationThrottles();
+    throttle_registry_->RegisterNavigationThrottles();
     base::UmaHistogramTimes(
         base::StrCat({"Navigation.RegisterNavigationThrottlesTime.",
                       IsInMainFrame() ? "MainFrame" : "Subframe"}),
@@ -8017,7 +8021,7 @@
        IsInMainFrame() ? "MainFrame" : "SubFrame"}));
   // Notify each throttle of the request.
   throttle_runner_->ProcessNavigationEvent(
-      NavigationThrottleRunner::Event::kWillStartRequest);
+      NavigationThrottleEvent::kWillStartRequest);
   // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
   // by the previous call.
 }
@@ -8050,7 +8054,7 @@
 
   // Notify each throttle of the request.
   throttle_runner_->ProcessNavigationEvent(
-      NavigationThrottleRunner::Event::kWillRedirectRequest);
+      NavigationThrottleEvent::kWillRedirectRequest);
   // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
   // by the previous call.
 }
@@ -8063,7 +8067,7 @@
 
   // Notify each throttle of the request.
   throttle_runner_->ProcessNavigationEvent(
-      NavigationThrottleRunner::Event::kWillFailRequest);
+      NavigationThrottleEvent::kWillFailRequest);
   // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
   // by the previous call.
 }
@@ -8082,7 +8086,7 @@
 
   // Notify each throttle of the response.
   throttle_runner_->ProcessNavigationEvent(
-      NavigationThrottleRunner::Event::kWillProcessResponse);
+      NavigationThrottleEvent::kWillProcessResponse);
 
   // `this` may have been deleted by the previous call.
   if (!this_ptr) {
@@ -8101,7 +8105,7 @@
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
   EnterChildTraceEvent("WillCommitWithoutUrlLoader", this);
 
-  throttle_runner_->RegisterNavigationThrottlesForCommitWithoutUrlLoader();
+  throttle_registry_->RegisterNavigationThrottlesForCommitWithoutUrlLoader();
 
   // `CommitNavigation()` expects to be called once the request has reached
   // at least `WILL_PROCESS_REPSONSE`. `WILL_COMMIT_WITHOUT_URL_LOADER` meets
@@ -8111,7 +8115,7 @@
   processing_navigation_throttle_ = true;
 
   throttle_runner_->ProcessNavigationEvent(
-      NavigationThrottleRunner::Event::kWillCommitWithoutUrlLoader);
+      NavigationThrottleEvent::kWillCommitWithoutUrlLoader);
 }
 
 bool NavigationRequest::IsSelfReferentialURL() {
@@ -9889,9 +9893,10 @@
     //
     // 2. By a document in the <fencedframe> frame tree. In this case the
     //    initiator policies are properly plumbed and should be used.
-    //    TODO(crbug.com/40258851): Use the initiator policies. On can
-    //    use `is_embedder_initiated_fenced_frame_navigation_` to discriminate
-    //    (1) from (2).
+    //
+    // TODO(crbug.com/40258851): Use the initiator policies. On can use
+    // `is_embedder_initiated_fenced_frame_navigation_` to discriminate (1) from
+    // (2).
     //
     // NOTE: For an embedder initiated fenced frame navigation that is subject
     // to private network access checks:
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index 132550fc..9160620 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -32,6 +32,7 @@
 #include "content/browser/renderer_host/cookie_access_observers.h"
 #include "content/browser/renderer_host/navigation_controller_impl.h"
 #include "content/browser/renderer_host/navigation_policy_container_builder.h"
+#include "content/browser/renderer_host/navigation_throttle_registry_impl.h"
 #include "content/browser/renderer_host/navigation_throttle_runner.h"
 #include "content/browser/renderer_host/navigation_type.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
@@ -111,7 +112,6 @@
 class CONTENT_EXPORT NavigationRequest
     : public NavigationHandle,
       public NavigationURLLoaderDelegate,
-      public NavigationThrottleRunner::Delegate,
       public CommitDeferringConditionRunner::Delegate,
       public FencedFrameURLMapping::MappingResultObserver,
       public mojom::NavigationRendererCancellationListener,
@@ -756,6 +756,11 @@
     return throttle_runner_.get();
   }
 
+  // Returns the underlying NavigationThrottleRegistry for tests to manipulate.
+  NavigationThrottleRegistry* GetNavigationThrottleRegistryForTesting() {
+    return throttle_registry_.get();
+  }
+
   // Simulates renderer cancelling the navigation.
   void RendererRequestedNavigationCancellationForTesting();
 
@@ -1720,6 +1725,13 @@
   std::optional<ukm::builders::NavigationTimeline>
   GetNavigationTimelineUkmBuilder();
 
+  // Called when the NavigationThrottleRunner is done processing the navigation
+  // event of type `event`. `result` is the final
+  // NavigationThrottle::ThrottleCheckResult for this event.
+  void OnNavigationEventProcessed(
+      NavigationThrottleEvent event,
+      NavigationThrottle::ThrottleCheckResult result);
+
  private:
   friend class NavigationRequestTest;
   FRIEND_TEST_ALL_PREFIXES(NavigationRequestTest, SanitizeRedirectsForCommit);
@@ -2053,11 +2065,6 @@
   // filtered by download_policy.
   void RecordDownloadUseCountersPostPolicyCheck();
 
-  // NavigationThrottleRunner::Delegate:
-  void OnNavigationEventProcessed(
-      NavigationThrottleRunner::Event event,
-      NavigationThrottle::ThrottleCheckResult result) override;
-
   void OnWillStartRequestProcessed(
       NavigationThrottle::ThrottleCheckResult result);
   void OnWillRedirectRequestProcessed(
@@ -2653,6 +2660,10 @@
   // The offset of the new document in the history.
   const int navigation_entry_offset_ = 0;
 
+  // Owns the NavigationThrottleRegistry associated with this navigation.
+  // This should outlive `throttle_runner_`.
+  std::unique_ptr<NavigationThrottleRegistryImpl> throttle_registry_;
+
   // Owns the NavigationThrottles associated with this navigation, and is
   // responsible for notifying them about the various navigation events.
   std::unique_ptr<NavigationThrottleRunner> throttle_runner_;
diff --git a/content/browser/renderer_host/navigation_request_unittest.cc b/content/browser/renderer_host/navigation_request_unittest.cc
index f78672b..7ded5ed 100644
--- a/content/browser/renderer_host/navigation_request_unittest.cc
+++ b/content/browser/renderer_host/navigation_request_unittest.cc
@@ -165,7 +165,7 @@
   TestNavigationThrottle* CreateTestNavigationThrottle(
       NavigationThrottle::ThrottleCheckResult result) {
     TestNavigationThrottle* test_throttle = new TestNavigationThrottle(
-        *GetNavigationRequest()->GetNavigationThrottleRunnerForTesting());
+        *GetNavigationRequest()->GetNavigationThrottleRegistryForTesting());
     test_throttle->SetResponseForAllMethods(TestNavigationThrottle::SYNCHRONOUS,
                                             result);
     GetNavigationRequest()->RegisterThrottleForTesting(
@@ -1225,7 +1225,6 @@
  public:
   PersistentOriginTrialNavigationRequestTest()
       : delegate_mock_(std::make_unique<OriginTrialsControllerDelegateMock>()) {
-
   }
   ~PersistentOriginTrialNavigationRequestTest() override = default;
 
diff --git a/content/browser/renderer_host/navigation_throttle_registry_impl.cc b/content/browser/renderer_host/navigation_throttle_registry_impl.cc
new file mode 100644
index 0000000..5966e63
--- /dev/null
+++ b/content/browser/renderer_host/navigation_throttle_registry_impl.cc
@@ -0,0 +1,196 @@
+// 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 "content/browser/renderer_host/navigation_throttle_registry_impl.h"
+
+#include "base/check_deref.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "content/browser/devtools/devtools_instrumentation.h"
+#include "content/browser/preloading/prefetch/contamination_delay_navigation_throttle.h"
+#include "content/browser/preloading/prerender/prerender_navigation_throttle.h"
+#include "content/browser/preloading/prerender/prerender_subframe_navigation_throttle.h"
+#include "content/browser/renderer_host/ancestor_throttle.h"
+#include "content/browser/renderer_host/back_forward_cache_subframe_navigation_throttle.h"
+#include "content/browser/renderer_host/blocked_scheme_navigation_throttle.h"
+#include "content/browser/renderer_host/http_error_navigation_throttle.h"
+#include "content/browser/renderer_host/isolated_web_app_throttle.h"
+#include "content/browser/renderer_host/mixed_content_navigation_throttle.h"
+#include "content/browser/renderer_host/navigation_request.h"
+#include "content/browser/renderer_host/navigator_delegate.h"
+#include "content/browser/renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.h"
+#include "content/browser/renderer_host/renderer_cancellation_throttle.h"
+#include "content/browser/renderer_host/subframe_history_navigation_throttle.h"
+#include "content/public/browser/navigation_handle.h"
+
+#if !BUILDFLAG(IS_ANDROID)
+#include "content/browser/picture_in_picture/document_picture_in_picture_navigation_throttle.h"
+#endif  // !BUILDFLAG(IS_ANDROID)
+
+namespace content {
+
+NavigationThrottleRegistryBase::~NavigationThrottleRegistryBase() = default;
+
+NavigationThrottleRegistryImpl::NavigationThrottleRegistryImpl(
+    NavigationRequest* navigation_request)
+    : navigation_request_(CHECK_DEREF(navigation_request)) {}
+
+NavigationThrottleRegistryImpl::~NavigationThrottleRegistryImpl() = default;
+
+void NavigationThrottleRegistryImpl::RegisterNavigationThrottles() {
+  TRACE_EVENT0("navigation",
+               "NavigationThrottleRegistryImpl::RegisterNavigationThrottles");
+  // Note: `throttles_` might not be empty. Some NavigationThrottles might have
+  // been registered with RegisterThrottleForTesting. These must reside at the
+  // end of `throttles_`. TestNavigationManagerThrottle expects that the
+  // NavigationThrottles added for test are the last NavigationThrottles to
+  // execute. Take them out while appending the rest of the
+  // NavigationThrottles.
+  std::vector<std::unique_ptr<NavigationThrottle>> testing_throttles =
+      std::move(throttles_);
+
+  // The NavigationRequest associated with the NavigationThrottles this
+  // NavigationThrottleRunner manages.
+  navigation_request_->GetDelegate()->CreateThrottlesForNavigation(*this);
+
+  // Check for renderer-inititated main frame navigations to blocked URL schemes
+  // (data, filesystem). This is done early as it may block the main frame
+  // navigation altogether.
+  BlockedSchemeNavigationThrottle::MaybeCreateAndAdd(*this);
+
+#if !BUILDFLAG(IS_ANDROID)
+  // Prevent cross-document navigations from document picture-in-picture
+  // windows.
+  DocumentPictureInPictureNavigationThrottle::MaybeCreateAndAdd(*this);
+#endif  // !BUILDFLAG(IS_ANDROID)
+
+  AncestorThrottle::CreateAndAdd(*this);
+
+  // Check for mixed content. This is done after the AncestorThrottle and the
+  // FormSubmissionThrottle so that when folks block mixed content with a CSP
+  // policy, they don't get a warning. They'll still get a warning in the
+  // console about CSP blocking the load.
+  MixedContentNavigationThrottle::CreateAndAdd(*this);
+
+  // Delay response processing for certain prefetch responses where it might
+  // otherwise reveal information about cross-site state.
+  ContaminationDelayNavigationThrottle::MaybeCreateAndAdd(*this);
+
+  // Block certain requests that are not permitted for prerendering.
+  PrerenderNavigationThrottle::MaybeCreateAndAdd(*this);
+
+  // Defer cross-origin subframe loading during prerendering state.
+  PrerenderSubframeNavigationThrottle::MaybeCreateAndAdd(*this);
+
+  // Prevent navigations to/from Isolated Web Apps.
+  IsolatedWebAppThrottle::MaybeCreateAndAdd(*this);
+
+  devtools_instrumentation::CreateAndAddNavigationThrottles(*this);
+
+  // Make main frame navigations with error HTTP status code and an empty body
+  // commit an error page instead. Note that this should take lower priority
+  // than other throttles that might care about those navigations, e.g.
+  // throttles handling pages with 407 errors that require extra authentication.
+  HttpErrorNavigationThrottle::MaybeCreateAndAdd(*this);
+
+  // Wait for renderer-initiated navigation cancelation window to end. This will
+  // wait for the JS task that starts the navigation to finish, so add it close
+  // to the end to not delay running other throttles.
+  RendererCancellationThrottle::MaybeCreateAndAdd(*this);
+
+  // Defer any cross-document subframe history navigations if there is an
+  // associated main-frame same-document history navigation in progress, until
+  // the main frame has had an opportunity to fire a navigate event in the
+  // renderer. If the navigate event cancels the history navigation, the
+  // subframe navigations should not proceed.
+  SubframeHistoryNavigationThrottle::MaybeCreateAndAdd(*this);
+
+  // Defer subframe navigation in bfcached page if it hasn't sent a network
+  // request.
+  // This must be the last throttle to run. See https://crrev.com/c/5316738.
+  BackForwardCacheSubframeNavigationThrottle::MaybeCreateAndAdd(*this);
+
+  // Add a throttle to manage top-frame navigations from a partitioned popin.
+  // See https://explainers-by-googlers.github.io/partitioned-popins/
+  PartitionedPopinsNavigationThrottle::MaybeCreateAndAdd(*this);
+  // DO NOT ADD any throttles after this line.
+
+  // Insert all testing NavigationThrottles last.
+  throttles_.insert(throttles_.end(),
+                    std::make_move_iterator(testing_throttles.begin()),
+                    std::make_move_iterator(testing_throttles.end()));
+
+  base::UmaHistogramCounts100("Navigation.ThrottleCount", throttles_.size());
+}
+
+void NavigationThrottleRegistryImpl::
+    RegisterNavigationThrottlesForCommitWithoutUrlLoader() {
+  // Note: `throttles_` might not be empty. Some NavigationThrottles might have
+  // been registered with RegisterThrottleForTesting. These must reside at the
+  // end of `throttles_`. TestNavigationManagerThrottle expects that the
+  // NavigationThrottles added for test are the last NavigationThrottles to
+  // execute. Take them out while appending the rest of the
+  // NavigationThrottles.
+  std::vector<std::unique_ptr<NavigationThrottle>> testing_throttles =
+      std::move(throttles_);
+
+  // Defer any same-document subframe history navigations if there is an
+  // associated main-frame same-document history navigation in progress, until
+  // the main frame has had an opportunity to fire a navigate event in the
+  // renderer. If the navigate event cancels the history navigation, the
+  // subframe navigations should not proceed.
+  SubframeHistoryNavigationThrottle::MaybeCreateAndAdd(*this);
+
+  // Defer cross-origin about:srcdoc subframe loading during prerendering state.
+  PrerenderSubframeNavigationThrottle::MaybeCreateAndAdd(*this);
+
+  // Defer subframe navigation in bfcached page.
+  BackForwardCacheSubframeNavigationThrottle::MaybeCreateAndAdd(*this);
+
+  RendererCancellationThrottle::MaybeCreateAndAdd(*this);
+
+  // Insert all testing NavigationThrottles last.
+  throttles_.insert(throttles_.end(),
+                    std::make_move_iterator(testing_throttles.begin()),
+                    std::make_move_iterator(testing_throttles.end()));
+}
+
+NavigationHandle& NavigationThrottleRegistryImpl::GetNavigationHandle() {
+  return *navigation_request_;
+}
+
+void NavigationThrottleRegistryImpl::AddThrottle(
+    std::unique_ptr<NavigationThrottle> navigation_throttle) {
+  CHECK(navigation_throttle);
+  TRACE_EVENT1("navigation", "NavigationThrottleRegistryImpl::AddThrottle",
+               "navigation_throttle", navigation_throttle->GetNameForLogging());
+  throttles_.push_back(std::move(navigation_throttle));
+}
+
+void NavigationThrottleRegistryImpl::MaybeAddThrottle(
+    std::unique_ptr<NavigationThrottle> navigation_throttle) {
+  if (navigation_throttle) {
+    AddThrottle(std::move(navigation_throttle));
+  }
+}
+
+void NavigationThrottleRegistryImpl::OnEventProcessed(
+    NavigationThrottleEvent event,
+    NavigationThrottle::ThrottleCheckResult result) {
+  navigation_request_->OnNavigationEventProcessed(event, result);
+}
+
+std::vector<std::unique_ptr<NavigationThrottle>>&
+NavigationThrottleRegistryImpl::GetThrottles() {
+  return throttles_;
+}
+
+NavigationThrottle& NavigationThrottleRegistryImpl::GetThrottleAtIndex(
+    size_t index) {
+  CHECK_LT(index, throttles_.size());
+  return *throttles_[index];
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/navigation_throttle_registry_impl.h b/content/browser/renderer_host/navigation_throttle_registry_impl.h
new file mode 100644
index 0000000..a327e5e
--- /dev/null
+++ b/content/browser/renderer_host/navigation_throttle_registry_impl.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 CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_THROTTLE_REGISTRY_IMPL_H_
+#define CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_THROTTLE_REGISTRY_IMPL_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/raw_ref.h"
+#include "base/memory/safety_checks.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "content/public/browser/navigation_throttle_registry.h"
+
+namespace content {
+
+class NavigationHandle;
+class NavigationRequest;
+
+// The different event types that can be processed by NavigationThrottles.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// This type is also used in the UKM as set in the RecordDeferTimeUKM().
+//
+// LINT.IfChange(NavigationThrottleEvent)
+enum class NavigationThrottleEvent {
+  kNoEvent = 0,
+  kWillStartRequest = 1,
+  kWillRedirectRequest = 2,
+  kWillFailRequest = 3,
+  kWillProcessResponse = 4,
+  kWillCommitWithoutUrlLoader = 5,
+  kMaxValue = kWillCommitWithoutUrlLoader,
+};
+// LINT.ThenChange(//tools/metrics/histograms/metadata/navigation/enums.xml:NavigationThrottleEvent)
+
+
+class CONTENT_EXPORT NavigationThrottleRegistryBase
+    : public NavigationThrottleRegistry {
+ public:
+  ~NavigationThrottleRegistryBase() override;
+
+  // Called when the NavigationThrottleRunner is done processing the navigation
+  // event of type `event`. `result` is the final
+  // NavigationThrottle::ThrottleCheckResult for this event.
+  virtual void OnEventProcessed(
+      NavigationThrottleEvent event,
+      NavigationThrottle::ThrottleCheckResult result) = 0;
+
+  // Returns the list of NavigationThrottles registered for this navigation.
+  virtual std::vector<std::unique_ptr<NavigationThrottle>>& GetThrottles() = 0;
+
+  // Returns the NavigationThrottle at the given `index`. The `index` should
+  // be in a valid range.
+  virtual NavigationThrottle& GetThrottleAtIndex(size_t index) = 0;
+};
+
+class NavigationThrottleRegistryImpl : public NavigationThrottleRegistryBase {
+  // Do not remove this macro!
+  // The macro is maintained by the memory safety team.
+  ADVANCED_MEMORY_SAFETY_CHECKS();
+
+ public:
+  explicit NavigationThrottleRegistryImpl(
+      NavigationRequest* navigation_request);
+  NavigationThrottleRegistryImpl(const NavigationThrottleRegistryImpl&) =
+      delete;
+  NavigationThrottleRegistryImpl& operator=(
+      const NavigationThrottleRegistryImpl&) = delete;
+  ~NavigationThrottleRegistryImpl() override;
+
+  // Registers the appropriate NavigationThrottles for a "standard" navigation
+  // (i.e., one with a URLLoader that goes through the
+  // WillSendRequest/WillProcessResponse callback sequence).
+  void RegisterNavigationThrottles();
+
+  // Registers the appropriate NavigationThrottles for a navigation that can
+  // immediately commit because no URLLoader is required (about:blank,
+  // about:srcdoc, and most same-document navigations).
+  void RegisterNavigationThrottlesForCommitWithoutUrlLoader();
+
+  // Implements NavigationThrottleRegistry:
+  NavigationHandle& GetNavigationHandle() override;
+  void AddThrottle(
+      std::unique_ptr<NavigationThrottle> navigation_throttle) override;
+  void MaybeAddThrottle(
+      std::unique_ptr<NavigationThrottle> navigation_throttle) override;
+
+  // Implements NavigationThrottleRegistryBase:
+  void OnEventProcessed(
+      NavigationThrottleEvent event,
+      NavigationThrottle::ThrottleCheckResult result) override;
+  std::vector<std::unique_ptr<NavigationThrottle>>& GetThrottles() override;
+  NavigationThrottle& GetThrottleAtIndex(size_t index) override;
+
+ private:
+  // Holds a reference to the NavigationRequest that owns this instance.
+  const raw_ref<NavigationRequest> navigation_request_;
+
+  // A list of Throttles registered for this navigation.
+  std::vector<std::unique_ptr<NavigationThrottle>> throttles_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_NAVIGATION_THROTTLE_REGISTRY_IMPL_H_
diff --git a/content/browser/renderer_host/navigation_throttle_runner.cc b/content/browser/renderer_host/navigation_throttle_runner.cc
index b027e6d9..a97af3e 100644
--- a/content/browser/renderer_host/navigation_throttle_runner.cc
+++ b/content/browser/renderer_host/navigation_throttle_runner.cc
@@ -11,94 +11,75 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/strings/strcat.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "content/browser/devtools/devtools_instrumentation.h"
-#include "content/browser/preloading/prefetch/contamination_delay_navigation_throttle.h"
-#include "content/browser/preloading/prerender/prerender_navigation_throttle.h"
-#include "content/browser/preloading/prerender/prerender_subframe_navigation_throttle.h"
-#include "content/browser/renderer_host/ancestor_throttle.h"
-#include "content/browser/renderer_host/back_forward_cache_subframe_navigation_throttle.h"
-#include "content/browser/renderer_host/blocked_scheme_navigation_throttle.h"
-#include "content/browser/renderer_host/http_error_navigation_throttle.h"
-#include "content/browser/renderer_host/isolated_web_app_throttle.h"
-#include "content/browser/renderer_host/mixed_content_navigation_throttle.h"
-#include "content/browser/renderer_host/navigation_request.h"
-#include "content/browser/renderer_host/navigator_delegate.h"
-#include "content/browser/renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.h"
-#include "content/browser/renderer_host/renderer_cancellation_throttle.h"
-#include "content/browser/renderer_host/subframe_history_navigation_throttle.h"
-#include "content/public/browser/navigation_handle.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
-#if !BUILDFLAG(IS_ANDROID)
-#include "content/browser/picture_in_picture/document_picture_in_picture_navigation_throttle.h"
-#endif  // !BUILDFLAG(IS_ANDROID)
-
 namespace content {
 
 namespace {
 
 NavigationThrottle::ThrottleCheckResult ExecuteNavigationEvent(
     NavigationThrottle* throttle,
-    NavigationThrottleRunner::Event event) {
+    NavigationThrottleEvent event) {
   switch (event) {
-    case NavigationThrottleRunner::Event::kNoEvent:
+    case NavigationThrottleEvent::kNoEvent:
       DUMP_WILL_BE_NOTREACHED();
       return NavigationThrottle::CANCEL_AND_IGNORE;
-    case NavigationThrottleRunner::Event::kWillStartRequest:
+    case NavigationThrottleEvent::kWillStartRequest:
       return throttle->WillStartRequest();
-    case NavigationThrottleRunner::Event::kWillRedirectRequest:
+    case NavigationThrottleEvent::kWillRedirectRequest:
       return throttle->WillRedirectRequest();
-    case NavigationThrottleRunner::Event::kWillFailRequest:
+    case NavigationThrottleEvent::kWillFailRequest:
       return throttle->WillFailRequest();
-    case NavigationThrottleRunner::Event::kWillProcessResponse:
+    case NavigationThrottleEvent::kWillProcessResponse:
       return throttle->WillProcessResponse();
-    case NavigationThrottleRunner::Event::kWillCommitWithoutUrlLoader:
+    case NavigationThrottleEvent::kWillCommitWithoutUrlLoader:
       return throttle->WillCommitWithoutUrlLoader();
   }
   NOTREACHED();
 }
 
-const char* GetEventName(NavigationThrottleRunner::Event event) {
+const char* GetEventName(NavigationThrottleEvent event) {
   switch (event) {
-    case NavigationThrottleRunner::Event::kNoEvent:
+    case NavigationThrottleEvent::kNoEvent:
       DUMP_WILL_BE_NOTREACHED();
       return "";
-    case NavigationThrottleRunner::Event::kWillStartRequest:
+    case NavigationThrottleEvent::kWillStartRequest:
       return "NavigationThrottle::WillStartRequest";
-    case NavigationThrottleRunner::Event::kWillRedirectRequest:
+    case NavigationThrottleEvent::kWillRedirectRequest:
       return "NavigationThrottle::WillRedirectRequest";
-    case NavigationThrottleRunner::Event::kWillFailRequest:
+    case NavigationThrottleEvent::kWillFailRequest:
       return "NavigationThrottle::WillFailRequest";
-    case NavigationThrottleRunner::Event::kWillProcessResponse:
+    case NavigationThrottleEvent::kWillProcessResponse:
       return "NavigationThrottle::WillProcessResponse";
-    case NavigationThrottleRunner::Event::kWillCommitWithoutUrlLoader:
+    case NavigationThrottleEvent::kWillCommitWithoutUrlLoader:
       return "NavigationThrottle::WillCommitWithoutUrlLoader";
   }
   NOTREACHED();
 }
 
-const char* GetEventNameForHistogram(NavigationThrottleRunner::Event event) {
+const char* GetEventNameForHistogram(NavigationThrottleEvent event) {
   switch (event) {
-    case NavigationThrottleRunner::Event::kNoEvent:
+    case NavigationThrottleEvent::kNoEvent:
       DUMP_WILL_BE_NOTREACHED();
       return "";
-    case NavigationThrottleRunner::Event::kWillStartRequest:
+    case NavigationThrottleEvent::kWillStartRequest:
       return "WillStartRequest";
-    case NavigationThrottleRunner::Event::kWillRedirectRequest:
+    case NavigationThrottleEvent::kWillRedirectRequest:
       return "WillRedirectRequest";
-    case NavigationThrottleRunner::Event::kWillFailRequest:
+    case NavigationThrottleEvent::kWillFailRequest:
       return "WillFailRequest";
-    case NavigationThrottleRunner::Event::kWillProcessResponse:
+    case NavigationThrottleEvent::kWillProcessResponse:
       return "WillProcessResponse";
-    case NavigationThrottleRunner::Event::kWillCommitWithoutUrlLoader:
+    case NavigationThrottleEvent::kWillCommitWithoutUrlLoader:
       return "WillCommitWithoutUrlLoader";
   }
   NOTREACHED();
 }
 
-base::TimeDelta RecordHistogram(NavigationThrottleRunner::Event event,
+base::TimeDelta RecordHistogram(NavigationThrottleEvent event,
                                 base::Time start,
                                 const std::string& metric_type) {
   base::TimeDelta delta = base::Time::Now() - start;
@@ -108,26 +89,25 @@
   return delta;
 }
 
-base::TimeDelta RecordDeferTimeHistogram(NavigationThrottleRunner::Event event,
+base::TimeDelta RecordDeferTimeHistogram(NavigationThrottleEvent event,
                                          base::Time start) {
   return RecordHistogram(event, start, "DeferTime");
 }
 
-void RecordExecutionTimeHistogram(NavigationThrottleRunner::Event event,
+void RecordExecutionTimeHistogram(NavigationThrottleEvent event,
                                   base::Time start) {
   RecordHistogram(event, start, "ExecutionTime");
 }
 
 }  // namespace
 
-NavigationThrottleRunner::NavigationThrottleRunner(Delegate* delegate,
-                                                   int64_t navigation_id,
-                                                   bool is_primary_main_frame)
-    : delegate_(delegate),
+NavigationThrottleRunner::NavigationThrottleRunner(
+    NavigationThrottleRegistryBase* registry,
+    int64_t navigation_id,
+    bool is_primary_main_frame)
+    : registry_(CHECK_DEREF(registry)),
       navigation_id_(navigation_id),
-      is_primary_main_frame_(is_primary_main_frame) {
-  CHECK(delegate_);
-}
+      is_primary_main_frame_(is_primary_main_frame) {}
 
 NavigationThrottleRunner::~NavigationThrottleRunner() {
   base::UmaHistogramTimes("Navigation.ThrottleTotalDeferTime",
@@ -140,31 +120,9 @@
                               defer_count_for_request_);
 }
 
-NavigationHandle& NavigationThrottleRunner::GetNavigationHandle() {
-  // TODO(https://crbug.com/412524375): Change the NavigationThrottleRunner
-  // to take a NavigationRequest instead of a Delegate. Then use the request
-  // to get the NavigationHandle safely here.
-  // See https://crrev.com/c/6478853/comment/4217a4c3_3e0f336b/.
-  return *static_cast<NavigationRequest*>(delegate_);
-}
-
-void NavigationThrottleRunner::AddThrottle(
-    std::unique_ptr<NavigationThrottle> navigation_throttle) {
-  CHECK(navigation_throttle);
-  TRACE_EVENT1("navigation", "NavigationThrottleRunner::AddThrottle",
-               "navigation_throttle", navigation_throttle->GetNameForLogging());
-  throttles_.push_back(std::move(navigation_throttle));
-}
-
-void NavigationThrottleRunner::MaybeAddThrottle(
-    std::unique_ptr<NavigationThrottle> navigation_throttle) {
-  if (navigation_throttle) {
-    AddThrottle(std::move(navigation_throttle));
-  }
-}
-
-void NavigationThrottleRunner::ProcessNavigationEvent(Event event) {
-  CHECK_NE(Event::kNoEvent, event);
+void NavigationThrottleRunner::ProcessNavigationEvent(
+    NavigationThrottleEvent event) {
+  CHECK_NE(NavigationThrottleEvent::kNoEvent, event);
   current_event_ = event;
   next_index_ = 0;
   ProcessInternal();
@@ -187,8 +145,8 @@
       RecordDeferTimeHistogram(current_event_, defer_start_time_);
   total_defer_duration_time_ += defer_time;
   defer_count_++;
-  if (current_event_ == Event::kWillStartRequest ||
-      current_event_ == Event::kWillRedirectRequest) {
+  if (current_event_ == NavigationThrottleEvent::kWillStartRequest ||
+      current_event_ == NavigationThrottleEvent::kWillRedirectRequest) {
     total_defer_duration_time_for_request_ += defer_time;
     defer_count_for_request_++;
   }
@@ -203,139 +161,16 @@
   ProcessInternal();
 }
 
-void NavigationThrottleRunner::RegisterNavigationThrottles() {
-  TRACE_EVENT0("navigation",
-               "NavigationThrottleRunner::RegisterNavigationThrottles");
-  // Note: |throttle_| might not be empty. Some NavigationThrottles might have
-  // been registered with RegisterThrottleForTesting. These must reside at the
-  // end of |throttles_|. TestNavigationManagerThrottle expects that the
-  // NavigationThrottles added for test are the last NavigationThrottles to
-  // execute. Take them out while appending the rest of the
-  // NavigationThrottles.
-  std::vector<std::unique_ptr<NavigationThrottle>> testing_throttles =
-      std::move(throttles_);
-
-  // The NavigationRequest associated with the NavigationThrottles this
-  // NavigationThrottleRunner manages.
-  // Unit tests that do not use NavigationRequest should never call
-  // RegisterNavigationThrottles as this function expects |delegate_| to be a
-  // NavigationRequest.
-  static_cast<NavigationRequest*>(delegate_)
-      ->GetDelegate()
-      ->CreateThrottlesForNavigation(*this);
-
-  // Check for renderer-inititated main frame navigations to blocked URL schemes
-  // (data, filesystem). This is done early as it may block the main frame
-  // navigation altogether.
-  BlockedSchemeNavigationThrottle::MaybeCreateAndAdd(*this);
-
-#if !BUILDFLAG(IS_ANDROID)
-  // Prevent cross-document navigations from document picture-in-picture
-  // windows.
-  DocumentPictureInPictureNavigationThrottle::MaybeCreateAndAdd(*this);
-#endif  // !BUILDFLAG(IS_ANDROID)
-
-  AncestorThrottle::CreateAndAdd(*this);
-
-  // Check for mixed content. This is done after the AncestorThrottle and the
-  // FormSubmissionThrottle so that when folks block mixed content with a CSP
-  // policy, they don't get a warning. They'll still get a warning in the
-  // console about CSP blocking the load.
-  MixedContentNavigationThrottle::CreateAndAdd(*this);
-
-  // Delay response processing for certain prefetch responses where it might
-  // otherwise reveal information about cross-site state.
-  ContaminationDelayNavigationThrottle::MaybeCreateAndAdd(*this);
-
-  // Block certain requests that are not permitted for prerendering.
-  PrerenderNavigationThrottle::MaybeCreateAndAdd(*this);
-
-  // Defer cross-origin subframe loading during prerendering state.
-  PrerenderSubframeNavigationThrottle::MaybeCreateAndAdd(*this);
-
-  // Prevent navigations to/from Isolated Web Apps.
-  IsolatedWebAppThrottle::MaybeCreateAndAdd(*this);
-
-  devtools_instrumentation::CreateAndAddNavigationThrottles(*this);
-
-  // Make main frame navigations with error HTTP status code and an empty body
-  // commit an error page instead. Note that this should take lower priority
-  // than other throttles that might care about those navigations, e.g.
-  // throttles handling pages with 407 errors that require extra authentication.
-  HttpErrorNavigationThrottle::MaybeCreateAndAdd(*this);
-
-  // Wait for renderer-initiated navigation cancelation window to end. This will
-  // wait for the JS task that starts the navigation to finish, so add it close
-  // to the end to not delay running other throttles.
-  RendererCancellationThrottle::MaybeCreateAndAdd(*this);
-
-  // Defer any cross-document subframe history navigations if there is an
-  // associated main-frame same-document history navigation in progress, until
-  // the main frame has had an opportunity to fire a navigate event in the
-  // renderer. If the navigate event cancels the history navigation, the
-  // subframe navigations should not proceed.
-  SubframeHistoryNavigationThrottle::MaybeCreateAndAdd(*this);
-
-  // Defer subframe navigation in bfcached page if it hasn't sent a network
-  // request.
-  // This must be the last throttle to run. See https://crrev.com/c/5316738.
-  BackForwardCacheSubframeNavigationThrottle::MaybeCreateAndAdd(*this);
-
-  // Add a throttle to manage top-frame navigations from a partitioned popin.
-  // See https://explainers-by-googlers.github.io/partitioned-popins/
-  PartitionedPopinsNavigationThrottle::MaybeCreateAndAdd(*this);
-  // DO NOT ADD any throttles after this line.
-
-  // Insert all testing NavigationThrottles last.
-  throttles_.insert(throttles_.end(),
-                    std::make_move_iterator(testing_throttles.begin()),
-                    std::make_move_iterator(testing_throttles.end()));
-
-  base::UmaHistogramCounts100("Navigation.ThrottleCount", throttles_.size());
-}
-
-void NavigationThrottleRunner::
-    RegisterNavigationThrottlesForCommitWithoutUrlLoader() {
-  // Note: |throttle_| might not be empty. Some NavigationThrottles might have
-  // been registered with RegisterThrottleForTesting. These must reside at the
-  // end of |throttles_|. TestNavigationManagerThrottle expects that the
-  // NavigationThrottles added for test are the last NavigationThrottles to
-  // execute. Take them out while appending the rest of the
-  // NavigationThrottles.
-  std::vector<std::unique_ptr<NavigationThrottle>> testing_throttles =
-      std::move(throttles_);
-
-  // Defer any same-document subframe history navigations if there is an
-  // associated main-frame same-document history navigation in progress, until
-  // the main frame has had an opportunity to fire a navigate event in the
-  // renderer. If the navigate event cancels the history navigation, the
-  // subframe navigations should not proceed.
-  SubframeHistoryNavigationThrottle::MaybeCreateAndAdd(*this);
-
-  // Defer cross-origin about:srcdoc subframe loading during prerendering state.
-  PrerenderSubframeNavigationThrottle::MaybeCreateAndAdd(*this);
-
-  // Defer subframe navigation in bfcached page.
-  BackForwardCacheSubframeNavigationThrottle::MaybeCreateAndAdd(*this);
-
-  RendererCancellationThrottle::MaybeCreateAndAdd(*this);
-
-  // Insert all testing NavigationThrottles last.
-  throttles_.insert(throttles_.end(),
-                    std::make_move_iterator(testing_throttles.begin()),
-                    std::make_move_iterator(testing_throttles.end()));
-}
-
 NavigationThrottle* NavigationThrottleRunner::GetDeferringThrottle() const {
   if (next_index_ == 0) {
     return nullptr;
   }
-  return throttles_[next_index_ - 1].get();
+  return &registry_->GetThrottleAtIndex(next_index_ - 1);
 }
 
 void NavigationThrottleRunner::ProcessInternal() {
   TRACE_EVENT0("navigation", "NavigationThrottleRunner::ProcessInternal");
-  CHECK_NE(Event::kNoEvent, current_event_);
+  CHECK_NE(NavigationThrottleEvent::kNoEvent, current_event_);
   base::Time start_time = base::Time::Now();
   if (!event_process_start_time_.has_value()) {
     event_process_start_time_ = start_time;
@@ -348,16 +183,17 @@
   // events need to be able to use the navigation id safely in such a case.
   int64_t local_navigation_id = navigation_id_;
 
-  for (size_t i = next_index_; i < throttles_.size(); ++i) {
+  auto& throttles = registry_->GetThrottles();
+  for (size_t i = next_index_; i < throttles.size(); ++i) {
     TRACE_EVENT0("navigation",
                  "NavigationThrottleRunner::ProcessInternal.loop");
     TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
         "navigation", GetEventName(current_event_), local_navigation_id,
-        "throttle", throttles_[i]->GetNameForLogging());
+        "throttle", throttles[i]->GetNameForLogging());
 
     base::Time start = base::Time::Now();
     NavigationThrottle::ThrottleCheckResult result =
-        ExecuteNavigationEvent(throttles_[i].get(), current_event_);
+        ExecuteNavigationEvent(throttles[i].get(), current_event_);
     if (!weak_ref) {
       // The NavigationThrottle execution has destroyed this
       // NavigationThrottleRunner. Return immediately.
@@ -381,7 +217,7 @@
       case NavigationThrottle::CANCEL_AND_IGNORE:
         next_index_ = 0;
         event_process_start_time_.reset();
-        InformDelegate(result);
+        InformRegistry(result);
         return;
 
       case NavigationThrottle::DEFER:
@@ -407,18 +243,18 @@
       end_time - *event_process_start_time_);
   event_process_start_time_.reset();
   next_index_ = 0;
-  InformDelegate(NavigationThrottle::PROCEED);
+  InformRegistry(NavigationThrottle::PROCEED);
 }
 
-void NavigationThrottleRunner::InformDelegate(
+void NavigationThrottleRunner::InformRegistry(
     const NavigationThrottle::ThrottleCheckResult& result) {
   // Now that the event has executed, reset the current event to kNoEvent since
   // we're no longer processing any event. Do it before the call to the
   // delegate, as it might lead to the deletion of this
   // NavigationThrottleRunner.
-  Event event = current_event_;
-  current_event_ = Event::kNoEvent;
-  delegate_->OnNavigationEventProcessed(event, result);
+  NavigationThrottleEvent event = current_event_;
+  current_event_ = NavigationThrottleEvent::kNoEvent;
+  registry_->OnEventProcessed(event, result);
   // DO NOT ADD CODE AFTER THIS. The NavigationThrottleRunner might have been
   // deleted by the previous call.
 }
diff --git a/content/browser/renderer_host/navigation_throttle_runner.h b/content/browser/renderer_host/navigation_throttle_runner.h
index 21f7fb2..6976574 100644
--- a/content/browser/renderer_host/navigation_throttle_runner.h
+++ b/content/browser/renderer_host/navigation_throttle_runner.h
@@ -14,74 +14,35 @@
 #include "base/memory/safety_checks.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "content/browser/renderer_host/navigation_throttle_registry_impl.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/navigation_throttle.h"
-#include "content/public/browser/navigation_throttle_registry.h"
 
 namespace content {
 
-class NavigationHandle;
-
-// This class owns the set of NavigationThrottles added to a NavigationHandle.
-// It is responsible for calling the various sets of events on its
-// NavigationThrottle, and notifying its delegate of the results of said events.
-// TODO(https://crbug.com/412524375): Currently this class implements
-// NavigationThrottleRegistry, but this will be factored out to a separate
-// NavigationThrottleRegistryImpl class, and will hold common logic for the
-// legacy NavigationThrottleRunner, and the new NavigationThrottleRunner2.
-class CONTENT_EXPORT NavigationThrottleRunner
-    : public NavigationThrottleRegistry {
+// This class collaborates with NavigationThrottleRegistry that owns the set of
+// NavigationThrottles added to an underlying navigation, and is responsible for
+// calling the various sets of events on its NavigationThrottles, and notifying
+// its delegate of the results of said events.
+class CONTENT_EXPORT NavigationThrottleRunner {
   // Do not remove this macro!
   // The macro is maintained by the memory safety team.
   ADVANCED_MEMORY_SAFETY_CHECKS();
 
  public:
-  // The different event types that can be processed by NavigationThrottles.
-  // These values are persisted to logs. Entries should not be renumbered and
-  // numeric values should never be reused.
-  // This type is also used in the UKM as set in the RecordDeferTimeUKM().
-  //
-  // LINT.IfChange(Event)
-  enum class Event {
-    kNoEvent = 0,
-    kWillStartRequest = 1,
-    kWillRedirectRequest = 2,
-    kWillFailRequest = 3,
-    kWillProcessResponse = 4,
-    kWillCommitWithoutUrlLoader = 5,
-    kMaxValue = kWillCommitWithoutUrlLoader,
-  };
-  // LINT.ThenChange(//tools/metrics/histograms/metadata/navigation/enums.xml:NavigationThrottleEvent)
-
-  class Delegate {
-   public:
-    // Called when the NavigationThrottleRunner is done processing the
-    // navigation event of type |event|. |result| is the final
-    // NavigationThrottle::ThrottleCheckResult for this event.
-    virtual void OnNavigationEventProcessed(
-        Event event,
-        NavigationThrottle::ThrottleCheckResult result) = 0;
-  };
-
-  NavigationThrottleRunner(Delegate* delegate,
+  // `registry` should outlive this instance.
+  NavigationThrottleRunner(NavigationThrottleRegistryBase* registry,
                            int64_t navigation_id,
                            bool is_primary_main_frame);
 
   NavigationThrottleRunner(const NavigationThrottleRunner&) = delete;
   NavigationThrottleRunner& operator=(const NavigationThrottleRunner&) = delete;
 
-  ~NavigationThrottleRunner() override;
-
-  // Implements NavigationThrottleRegistry:
-  NavigationHandle& GetNavigationHandle() override;
-  void AddThrottle(
-      std::unique_ptr<NavigationThrottle> navigation_throttle) override;
-  void MaybeAddThrottle(
-      std::unique_ptr<NavigationThrottle> navigation_throttle) override;
+  ~NavigationThrottleRunner();
 
   // Will call the appropriate NavigationThrottle function based on |event| on
   // all NavigationThrottles owned by this NavigationThrottleRunner.
-  void ProcessNavigationEvent(Event event);
+  void ProcessNavigationEvent(NavigationThrottleEvent event);
 
   // Resumes calling the appropriate NavigationThrottle functions for |event_|
   // on all NavigationThrottles that have not yet been notified.
@@ -94,16 +55,6 @@
   // deferring NavigationThrottle do the resuming.
   void CallResumeForTesting();
 
-  // Registers the appropriate NavigationThrottles are added for a "standard"
-  // navigation (i.e., one with a URLLoader that goes through the
-  // WillSendRequest/WillProcessResponse callback sequence).
-  void RegisterNavigationThrottles();
-
-  // Registers the appropriate NavigationThrottles for a navigation that can
-  // immediately commit because no URLLoader is required (about:blank,
-  // about:srcdoc, and most same-document navigations).
-  void RegisterNavigationThrottlesForCommitWithoutUrlLoader();
-
   // Returns the throttle that is currently deferring the navigation (i.e. the
   // throttle at index |next_index_ -1|). If the handle is not deferred, returns
   // nullptr;
@@ -115,15 +66,12 @@
 
  private:
   void ProcessInternal();
-  void InformDelegate(const NavigationThrottle::ThrottleCheckResult& result);
+  void InformRegistry(const NavigationThrottle::ThrottleCheckResult& result);
 
   // Records UKM about the deferring throttle when the navigation is resumed.
   void RecordDeferTimeUKM();
 
-  const raw_ptr<Delegate> delegate_;
-
-  // A list of Throttles registered for this navigation.
-  std::vector<std::unique_ptr<NavigationThrottle>> throttles_;
+  const raw_ref<NavigationThrottleRegistryBase> registry_;
 
   // The index of the next throttle to check.
   size_t next_index_;
@@ -156,7 +104,8 @@
   base::OnceClosure first_deferral_callback_for_testing_;
 
   // The event currently being processed.
-  Event current_event_ = Event::kNoEvent;
+  NavigationThrottleEvent current_event_ =
+      NavigationThrottleEvent::kNoEvent;
 
   // Whether the navigation is in the primary main frame.
   bool is_primary_main_frame_ = false;
diff --git a/content/browser/renderer_host/navigation_throttle_runner_unittest.cc b/content/browser/renderer_host/navigation_throttle_runner_unittest.cc
index 78c742a..a44c16da 100644
--- a/content/browser/renderer_host/navigation_throttle_runner_unittest.cc
+++ b/content/browser/renderer_host/navigation_throttle_runner_unittest.cc
@@ -9,10 +9,10 @@
 #include "base/functional/bind.h"
 #include "base/metrics/metrics_hashes.h"
 #include "components/ukm/test_ukm_recorder.h"
+#include "content/browser/renderer_host/navigation_throttle_registry_impl.h"
 #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"
@@ -63,10 +63,11 @@
 };
 
 class NavigationThrottleRunnerTest : public RenderViewHostTestHarness,
-                                     public NavigationThrottleRunner::Delegate {
+                                     public NavigationThrottleRegistryBase {
  public:
   NavigationThrottleRunnerTest()
       : delegate_result_(NavigationThrottle::DEFER) {}
+  ~NavigationThrottleRunnerTest() override = default;
 
   void SetUp() override {
     RenderViewHostTestHarness::SetUp();
@@ -75,10 +76,10 @@
 
   void Resume() { runner_->CallResumeForTesting(); }
 
-  void SimulateEvent(NavigationThrottleRunner::Event event) {
+  void SimulateEvent(NavigationThrottleEvent event) {
     was_delegate_notified_ = false;
     delegate_result_ = NavigationThrottle::DEFER;
-    observer_last_event_ = NavigationThrottleRunner::Event::kNoEvent;
+    observer_last_event_ = NavigationThrottleEvent::kNoEvent;
     runner_->ProcessNavigationEvent(event);
   }
 
@@ -90,7 +91,7 @@
     return delegate_result_;
   }
 
-  NavigationThrottleRunner::Event observer_last_event() const {
+  NavigationThrottleEvent observer_last_event() const {
     return observer_last_event_;
   }
 
@@ -110,36 +111,36 @@
   }
 
   void CheckNotifiedOfEvent(TestNavigationThrottle* throttle,
-                            NavigationThrottleRunner::Event event) {
-    if (event == NavigationThrottleRunner::Event::kWillStartRequest) {
+                            NavigationThrottleEvent event) {
+    if (event == NavigationThrottleEvent::kWillStartRequest) {
       CHECK_EQ(1, throttle->GetCallCount(
                       TestNavigationThrottle::WILL_START_REQUEST));
     } else {
       CHECK_EQ(0, throttle->GetCallCount(
                       TestNavigationThrottle::WILL_START_REQUEST));
     }
-    if (event == NavigationThrottleRunner::Event::kWillRedirectRequest) {
+    if (event == NavigationThrottleEvent::kWillRedirectRequest) {
       CHECK_EQ(1, throttle->GetCallCount(
                       TestNavigationThrottle::WILL_REDIRECT_REQUEST));
     } else {
       CHECK_EQ(0, throttle->GetCallCount(
                       TestNavigationThrottle::WILL_REDIRECT_REQUEST));
     }
-    if (event == NavigationThrottleRunner::Event::kWillFailRequest) {
+    if (event == NavigationThrottleEvent::kWillFailRequest) {
       CHECK_EQ(
           1, throttle->GetCallCount(TestNavigationThrottle::WILL_FAIL_REQUEST));
     } else {
       CHECK_EQ(
           0, throttle->GetCallCount(TestNavigationThrottle::WILL_FAIL_REQUEST));
     }
-    if (event == NavigationThrottleRunner::Event::kWillProcessResponse) {
+    if (event == NavigationThrottleEvent::kWillProcessResponse) {
       CHECK_EQ(1, throttle->GetCallCount(
                       TestNavigationThrottle::WILL_PROCESS_RESPONSE));
     } else {
       CHECK_EQ(0, throttle->GetCallCount(
                       TestNavigationThrottle::WILL_PROCESS_RESPONSE));
     }
-    if (event == NavigationThrottleRunner::Event::kWillCommitWithoutUrlLoader) {
+    if (event == NavigationThrottleEvent::kWillCommitWithoutUrlLoader) {
       CHECK_EQ(1, throttle->GetCallCount(
                       TestNavigationThrottle::WILL_COMMIT_WITHOUT_URL_LOADER));
     } else {
@@ -152,12 +153,10 @@
   // synchronously return |result| on checks by default.
   TestNavigationThrottle* CreateTestNavigationThrottle(
       NavigationThrottle::ThrottleCheckResult result) {
-    TestNavigationThrottle* test_throttle =
-        new TestNavigationThrottle(registry_);
+    TestNavigationThrottle* test_throttle = new TestNavigationThrottle(*this);
     test_throttle->SetResponseForAllMethods(TestNavigationThrottle::SYNCHRONOUS,
                                             result);
-    runner_->AddThrottle(
-        std::unique_ptr<TestNavigationThrottle>(test_throttle));
+    AddThrottle(std::unique_ptr<TestNavigationThrottle>(test_throttle));
     return test_throttle;
   }
 
@@ -177,39 +176,51 @@
   // Creates and register a NavigationThrottle that will delete the
   // NavigationHandle in checks.
   void AddDeletingNavigationThrottle() {
-    runner_->AddThrottle(std::make_unique<DeletingNavigationThrottle>(
-        registry_,
-        base::BindRepeating(
-            &NavigationThrottleRunnerTest::ResetNavigationThrottleRunner,
-            base::Unretained(this))));
+    AddThrottle(std::make_unique<DeletingNavigationThrottle>(
+        *this, base::BindRepeating(
+                   &NavigationThrottleRunnerTest::ResetNavigationThrottleRunner,
+                   base::Unretained(this))));
   }
 
   ukm::TestUkmRecorder& test_ukm_recorder() { return test_ukm_recorder_; }
 
  private:
-  // NavigationThrottleRunner::Delegate:
-  void OnNavigationEventProcessed(
-      NavigationThrottleRunner::Event event,
+  // NavigationThrottleRegistry:
+  NavigationHandle& GetNavigationHandle() override { return handle_; }
+  void AddThrottle(
+      std::unique_ptr<NavigationThrottle> navigation_throttle) override {
+    throttles_.push_back(std::move(navigation_throttle));
+  }
+  void MaybeAddThrottle(
+      std::unique_ptr<NavigationThrottle> navigation_throttle) override {
+    if (navigation_throttle) {
+      AddThrottle(std::move(navigation_throttle));
+    }
+  }
+  // NavigationThrottleRegistryBase:
+  void OnEventProcessed(
+      NavigationThrottleEvent event,
       NavigationThrottle::ThrottleCheckResult result) override {
     DCHECK(!was_delegate_notified_);
     delegate_result_ = result;
     was_delegate_notified_ = true;
     observer_last_event_ = event;
   }
+  std::vector<std::unique_ptr<NavigationThrottle>>& GetThrottles() override {
+    return throttles_;
+  }
+  NavigationThrottle& GetThrottleAtIndex(size_t index) override {
+    EXPECT_LT(index, throttles_.size());
+    return *throttles_[index];
+  }
 
   void ResetNavigationThrottleRunner() { runner_.reset(); }
 
   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::vector<std::unique_ptr<NavigationThrottle>> throttles_;
   std::unique_ptr<NavigationThrottleRunner> runner_;
-  NavigationThrottleRunner::Event observer_last_event_ =
-      NavigationThrottleRunner::Event::kNoEvent;
+  NavigationThrottleEvent observer_last_event_ =
+      NavigationThrottleEvent::kNoEvent;
   bool was_delegate_notified_ = false;
   NavigationThrottle::ThrottleCheckResult delegate_result_;
   ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
@@ -217,23 +228,23 @@
 
 class NavigationThrottleRunnerTestWithEvent
     : public NavigationThrottleRunnerTest,
-      public testing::WithParamInterface<NavigationThrottleRunner::Event> {
+      public testing::WithParamInterface<NavigationThrottleEvent> {
  public:
-  NavigationThrottleRunnerTestWithEvent() : NavigationThrottleRunnerTest() {}
-  ~NavigationThrottleRunnerTestWithEvent() override {}
+  NavigationThrottleRunnerTestWithEvent() = default;
+  ~NavigationThrottleRunnerTestWithEvent() override = default;
   void SetUp() override {
     NavigationThrottleRunnerTest::SetUp();
     event_ = GetParam();
   }
 
-  NavigationThrottleRunner::Event event() const { return event_; }
+  NavigationThrottleEvent event() const { return event_; }
 
   void CheckNotified(TestNavigationThrottle* throttle) {
     CheckNotifiedOfEvent(throttle, event());
   }
 
  private:
-  NavigationThrottleRunner::Event event_;
+  NavigationThrottleEvent event_;
 };
 
 // Checks that a navigation deferred by a NavigationThrottle can be properly
@@ -286,27 +297,25 @@
 INSTANTIATE_TEST_SUITE_P(
     AllEvents,
     NavigationThrottleRunnerTestWithEvent,
-    ::testing::Values(
-        NavigationThrottleRunner::Event::kWillStartRequest,
-        NavigationThrottleRunner::Event::kWillRedirectRequest,
-        NavigationThrottleRunner::Event::kWillFailRequest,
-        NavigationThrottleRunner::Event::kWillProcessResponse,
-        NavigationThrottleRunner::Event::kWillCommitWithoutUrlLoader));
+    ::testing::Values(NavigationThrottleEvent::kWillStartRequest,
+                      NavigationThrottleEvent::kWillRedirectRequest,
+                      NavigationThrottleEvent::kWillFailRequest,
+                      NavigationThrottleEvent::kWillProcessResponse,
+                      NavigationThrottleEvent::kWillCommitWithoutUrlLoader));
 
 class NavigationThrottleRunnerTestWithEventAndAction
     : public NavigationThrottleRunnerTest,
       public testing::WithParamInterface<
-          std::tuple<NavigationThrottleRunner::Event,
+          std::tuple<NavigationThrottleEvent,
                      NavigationThrottle::ThrottleAction>> {
  public:
-  NavigationThrottleRunnerTestWithEventAndAction()
-      : NavigationThrottleRunnerTest() {}
-  ~NavigationThrottleRunnerTestWithEventAndAction() override {}
+  NavigationThrottleRunnerTestWithEventAndAction() = default;
+  ~NavigationThrottleRunnerTestWithEventAndAction() override = default;
   void SetUp() override {
     NavigationThrottleRunnerTest::SetUp();
     std::tie(event_, action_) = GetParam();
   }
-  NavigationThrottleRunner::Event event() const { return event_; }
+  NavigationThrottleEvent event() const { return event_; }
   NavigationThrottle::ThrottleAction action() const { return action_; }
 
   void CheckNotified(TestNavigationThrottle* throttle) {
@@ -314,7 +323,7 @@
   }
 
  private:
-  NavigationThrottleRunner::Event event_;
+  NavigationThrottleEvent event_;
   NavigationThrottle::ThrottleAction action_;
 };
 
@@ -437,12 +446,11 @@
     AllEvents,
     NavigationThrottleRunnerTestWithEventAndAction,
     ::testing::Combine(
-        ::testing::Values(
-            NavigationThrottleRunner::Event::kWillStartRequest,
-            NavigationThrottleRunner::Event::kWillRedirectRequest,
-            NavigationThrottleRunner::Event::kWillFailRequest,
-            NavigationThrottleRunner::Event::kWillProcessResponse,
-            NavigationThrottleRunner::Event::kWillCommitWithoutUrlLoader),
+        ::testing::Values(NavigationThrottleEvent::kWillStartRequest,
+                          NavigationThrottleEvent::kWillRedirectRequest,
+                          NavigationThrottleEvent::kWillFailRequest,
+                          NavigationThrottleEvent::kWillProcessResponse,
+                          NavigationThrottleEvent::kWillCommitWithoutUrlLoader),
         ::testing::Values(NavigationThrottle::PROCEED,
                           NavigationThrottle::CANCEL,
                           NavigationThrottle::CANCEL_AND_IGNORE,
@@ -453,18 +461,17 @@
 class NavigationThrottleRunnerTestWithEventAndError
     : public NavigationThrottleRunnerTest,
       public testing::WithParamInterface<
-          std::tuple<NavigationThrottleRunner::Event,
+          std::tuple<NavigationThrottleEvent,
                      net::Error,
                      std::optional<std::string>>> {
  public:
-  NavigationThrottleRunnerTestWithEventAndError()
-      : NavigationThrottleRunnerTest() {}
-  ~NavigationThrottleRunnerTestWithEventAndError() override {}
+  NavigationThrottleRunnerTestWithEventAndError() = default;
+  ~NavigationThrottleRunnerTestWithEventAndError() override = default;
   void SetUp() override {
     NavigationThrottleRunnerTest::SetUp();
     std::tie(event_, error_, custom_error_page_) = GetParam();
   }
-  NavigationThrottleRunner::Event event() const { return event_; }
+  NavigationThrottleEvent event() const { return event_; }
   net::Error error() const { return error_; }
   const std::optional<std::string>& custom_error_page() const {
     return custom_error_page_;
@@ -475,7 +482,7 @@
   }
 
  private:
-  NavigationThrottleRunner::Event event_;
+  NavigationThrottleEvent event_;
   net::Error error_;
   std::optional<std::string> custom_error_page_ = std::nullopt;
 };
@@ -518,12 +525,11 @@
     AllEvents,
     NavigationThrottleRunnerTestWithEventAndError,
     ::testing::Combine(
-        ::testing::Values(
-            NavigationThrottleRunner::Event::kWillStartRequest,
-            NavigationThrottleRunner::Event::kWillRedirectRequest,
-            NavigationThrottleRunner::Event::kWillFailRequest,
-            NavigationThrottleRunner::Event::kWillProcessResponse,
-            NavigationThrottleRunner::Event::kWillCommitWithoutUrlLoader),
+        ::testing::Values(NavigationThrottleEvent::kWillStartRequest,
+                          NavigationThrottleEvent::kWillRedirectRequest,
+                          NavigationThrottleEvent::kWillFailRequest,
+                          NavigationThrottleEvent::kWillProcessResponse,
+                          NavigationThrottleEvent::kWillCommitWithoutUrlLoader),
         ::testing::Values(net::ERR_BLOCKED_BY_ADMINISTRATOR, net::ERR_ABORTED),
         ::testing::Values(std::nullopt, "<html><body>test</body></html>")));
 
diff --git a/content/browser/renderer_host/navigator_delegate.h b/content/browser/renderer_host/navigator_delegate.h
index 645edb8..5ba2ce39 100644
--- a/content/browser/renderer_host/navigator_delegate.h
+++ b/content/browser/renderer_host/navigator_delegate.h
@@ -35,6 +35,7 @@
 class NavigationRequest;
 class NavigationThrottleRegistry;
 class RenderFrameHostImpl;
+class WebContents;
 struct LoadCommittedDetails;
 struct OpenURLParams;
 
diff --git a/content/browser/renderer_host/p2p/socket_dispatcher_host.h b/content/browser/renderer_host/p2p/socket_dispatcher_host.h
index bf918d9f..d1300efd 100644
--- a/content/browser/renderer_host/p2p/socket_dispatcher_host.h
+++ b/content/browser/renderer_host/p2p/socket_dispatcher_host.h
@@ -13,6 +13,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/task/sequenced_task_runner.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/public/browser/render_process_host.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 00ef9be..113ce66 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -55,6 +55,7 @@
 #include "base/timer/timer.h"
 #include "base/trace_event/optional_trace_event.h"
 #include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_id_helper.h"
 #include "base/types/optional_util.h"
 #include "base/uuid.h"
 #include "build/build_config.h"
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index 2e91a52..a24665b 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -24,9 +24,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
 #include "base/timer/elapsed_timer.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/named_trigger.h"
-#include "base/trace_event/trace_event.h"
 #include "base/trace_event/typed_macros.h"
 #include "base/types/cxx23_to_underlying.h"
 #include "base/types/expected.h"
diff --git a/content/browser/renderer_host/spare_render_process_host_manager_impl.cc b/content/browser/renderer_host/spare_render_process_host_manager_impl.cc
index debe95af..69682bb 100644
--- a/content/browser/renderer_host/spare_render_process_host_manager_impl.cc
+++ b/content/browser/renderer_host/spare_render_process_host_manager_impl.cc
@@ -51,6 +51,8 @@
              "kSpareRPHKeepOneAliveOnMemoryPressure",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+constexpr char kSpareProcessMaybeTakeActionUmaName[] =
+    "BrowserRenderProcessHost.SpareProcessMaybeTakeAction";
 constexpr char kSpareRendererTakenTimeSinceCreation[] =
     "BrowserRenderProcessHost.SpareRendererTaken.TimeSinceCreation";
 constexpr char kSpareRendererTakenIsReady[] =
@@ -266,10 +268,18 @@
   }
 }
 
-void LogSpareProcessTakeActionUMAs(RenderProcessHost* host,
-                                   SpareProcessMaybeTakeAction action) {
-  base::UmaHistogramEnumeration(
-      "BrowserRenderProcessHost.SpareProcessMaybeTakeAction", action);
+void LogSpareProcessTakeActionUMAs(
+    RenderProcessHost* host,
+    SpareProcessMaybeTakeAction action,
+    const ProcessAllocationContext& allocation_context) {
+  base::UmaHistogramEnumeration(kSpareProcessMaybeTakeActionUmaName, action);
+  if (allocation_context.source ==
+      ProcessAllocationSource::kNavigationRequest) {
+    base::UmaHistogramEnumeration(
+        base::StrCat(
+            {kSpareProcessMaybeTakeActionUmaName, ".NavigationRequest"}),
+        action);
+  }
   if (action == SpareProcessMaybeTakeAction::kSpareTaken) {
     CHECK(host);
     base::UmaHistogramBoolean(kSpareRendererTakenIsReady, host->IsReady());
@@ -593,7 +603,7 @@
   } else {
     action = SpareProcessMaybeTakeAction::kSpareTaken;
   }
-  LogSpareProcessTakeActionUMAs(next_spare_rph, action);
+  LogSpareProcessTakeActionUMAs(next_spare_rph, action, allocation_context);
 
   if (spare_renderer_maybe_take_timer_) {
     auto maybe_take_time = spare_renderer_maybe_take_timer_->Elapsed();
diff --git a/content/browser/renderer_host/unassigned_site_instance_browsertest.cc b/content/browser/renderer_host/unassigned_site_instance_browsertest.cc
index 863b1ac2..c4f9851 100644
--- a/content/browser/renderer_host/unassigned_site_instance_browsertest.cc
+++ b/content/browser/renderer_host/unassigned_site_instance_browsertest.cc
@@ -410,7 +410,7 @@
   EXPECT_TRUE(popup_si->HasSite());
   EXPECT_EQ(popup_si, original_si);
 
-  if (!AreAllSitesIsolatedForTesting()) {
+  if (!AreStrictSiteInstancesEnabled()) {
     EXPECT_TRUE(popup_si->IsDefaultSiteInstance());
   }
 
@@ -444,7 +444,7 @@
   EXPECT_TRUE(popup_si->HasSite());
   EXPECT_EQ(popup_si, original_si);
 
-  if (!AreAllSitesIsolatedForTesting()) {
+  if (!AreStrictSiteInstancesEnabled()) {
     EXPECT_TRUE(popup_si->IsDefaultSiteInstance());
   }
 
@@ -477,7 +477,7 @@
   EXPECT_TRUE(popup_si->HasSite());
   EXPECT_EQ(popup_si, original_si);
 
-  if (!AreAllSitesIsolatedForTesting()) {
+  if (!AreStrictSiteInstancesEnabled()) {
     EXPECT_TRUE(popup_si->IsDefaultSiteInstance());
   }
 
@@ -514,7 +514,7 @@
   EXPECT_TRUE(popup_si->HasSite());
   EXPECT_EQ(popup_si, original_si);
 
-  if (!AreAllSitesIsolatedForTesting()) {
+  if (!AreStrictSiteInstancesEnabled()) {
     EXPECT_TRUE(popup_si->IsDefaultSiteInstance());
   }
 
@@ -613,7 +613,7 @@
   EXPECT_TRUE(iframe_si->HasSite());
   EXPECT_EQ(iframe_si, original_si);
 
-  if (!AreAllSitesIsolatedForTesting()) {
+  if (!AreStrictSiteInstancesEnabled()) {
     EXPECT_TRUE(iframe_si->IsDefaultSiteInstance());
   }
 
@@ -647,7 +647,7 @@
   EXPECT_TRUE(iframe_si->HasSite());
   EXPECT_EQ(iframe_si, original_si);
 
-  if (!AreAllSitesIsolatedForTesting()) {
+  if (!AreStrictSiteInstancesEnabled()) {
     EXPECT_TRUE(iframe_si->IsDefaultSiteInstance());
   }
 
@@ -658,7 +658,7 @@
   scoped_refptr<SiteInstanceImpl> post_navigation_si =
       original_rfh->child_at(0)->current_frame_host()->GetSiteInstance();
 
-  if (AreAllSitesIsolatedForTesting()) {
+  if (AreStrictSiteInstancesEnabled()) {
     EXPECT_FALSE(post_navigation_si->HasSite());
     EXPECT_TRUE(post_navigation_si->IsRelatedSiteInstance(original_si.get()));
   } else {
@@ -690,7 +690,7 @@
   EXPECT_EQ(instance1,
             web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
   EXPECT_TRUE(instance1->HasSite());
-  if (AreAllSitesIsolatedForTesting()) {
+  if (AreStrictSiteInstancesEnabled()) {
     EXPECT_EQ(RegularUrlOriginMaybeWithPort(), instance1->GetSiteURL());
   } else {
     EXPECT_TRUE(instance1->IsDefaultSiteInstance());
@@ -724,7 +724,7 @@
   EXPECT_TRUE(NavigateToURL(shell(), other_regular_url));
   exit_observer.Wait();
 
-  if (AreAllSitesIsolatedForTesting()) {
+  if (AreStrictSiteInstancesEnabled()) {
     EXPECT_NE(instance1,
               web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
   } else {
@@ -746,6 +746,12 @@
     // In site-per-process, we cannot use foo.com's SiteInstance for a.com.
     EXPECT_FALSE(instance1->IsSuitableForUrlInfo(
         UrlInfo::CreateForTesting(embedder_defined_unassigned_url())));
+  } else if (AreStrictSiteInstancesEnabled()) {
+    // If neither foo.com nor a.com require dedicated processes, and we're using
+    // default SiteInstanceGroup instead of default SiteInstance, then we can
+    // use the same process.
+    EXPECT_TRUE(instance1->IsSuitableForUrlInfo(
+        UrlInfo::CreateForTesting(embedder_defined_unassigned_url())));
   } else {
     // Since |instance1| is a default SiteInstance AND this test explicitly
     // ensures that ShouldAssignSiteForURL(url1) will return false, |url1|
diff --git a/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc b/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc
index beec338d..e1a59ee2 100644
--- a/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc
+++ b/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc
@@ -4139,8 +4139,34 @@
                     ->GetProcess()
                     ->GetProcessLock()
                     .is_locked_to_site());
-    histogram_tester.ExpectUniqueSample("Navigation.ProcessReuseOnCOOP.None",
-                                        false, 1);
+
+    // The metric for why we failed to reuse the process will be reported
+    // differently depending on whether default SiteInstanceGroups are enabled.
+    // For more information, see how "Navigation.ProcessReuseOnCOOP" is emitted
+    // in `RenderFrameHostManager::GetSiteInstanceForNavigation()`. Importantly,
+    // the sample (i.e., whether actual process reuse happened) should be
+    // reported as false in both cases.
+    if (ShouldUseDefaultSiteInstanceGroup()) {
+      // With default SiteInstanceGroups, if we created a new candidate
+      // SiteInstance at the start of the navigation to `url_2`, then the reason
+      // will be recorded as "DifferentSiteInstance". This is the case when a
+      // speculative RFH is created, which requires either bfcache or
+      // RenderDocument (see also `speculative_rfh` expectations above).
+      // Otherwise, the reason will be recorded as
+      // "SameSiteNavigationInSingleWebContents".
+      if (IsBackForwardCacheEnabled() || ShouldCreateNewHostForAllFrames()) {
+        histogram_tester.ExpectUniqueSample(
+            "Navigation.ProcessReuseOnCOOP.DifferentSiteInstance", false, 1);
+      } else {
+        histogram_tester.ExpectUniqueSample(
+            "Navigation.ProcessReuseOnCOOP."
+            "SameSiteNavigationInSingleWebContents",
+            false, 1);
+      }
+    } else {
+      histogram_tester.ExpectUniqueSample("Navigation.ProcessReuseOnCOOP.None",
+                                          false, 1);
+    }
   } else {
     EXPECT_EQ(rph_id_2, rph_id_3);
 
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index c09abca5..d623c53 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -433,7 +433,7 @@
     wrapper()
         ->context()
         ->registry()
-        ->GetRemoteStorageControl()
+        .GetRemoteStorageControl()
         .FlushForTesting();
     content::RunAllTasksUntilIdle();
     wrapper_ = nullptr;
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc
index 3adfd43..aededd7 100644
--- a/content/browser/service_worker/service_worker_container_host.cc
+++ b/content/browser/service_worker/service_worker_container_host.cc
@@ -230,7 +230,7 @@
       service_worker_security_utils::GetCorrectStorageKeyForWebSecurityState(
           service_worker_client().key(), client_url);
 
-  context()->registry()->FindRegistrationForClientUrl(
+  context()->registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation, client_url, key,
       base::BindOnce(
           &ServiceWorkerContainerHostForClient::GetRegistrationComplete,
@@ -263,7 +263,7 @@
       "ServiceWorker", "ServiceWorkerContainerHost::GetRegistrations",
       TRACE_ID_WITH_SCOPE("ServiceWorkerContainerHost::GetRegistrations",
                           trace_id));
-  context()->registry()->GetRegistrationsForStorageKey(
+  context()->registry().GetRegistrationsForStorageKey(
       service_worker_client().key(),
       base::BindOnce(
           &ServiceWorkerContainerHostForClient::GetRegistrationsComplete,
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 8164648..6477b21 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -629,7 +629,7 @@
 void ServiceWorkerContextCore::DeleteForStorageKey(const blink::StorageKey& key,
                                                    StatusCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  registry()->GetRegistrationsForStorageKey(
+  registry().GetRegistrationsForStorageKey(
       key,
       base::BindOnce(
           &ServiceWorkerContextCore::DidGetRegistrationsForDeleteForStorageKey,
@@ -651,7 +651,7 @@
   // unload.
   std::vector<scoped_refptr<ServiceWorkerRegistration>>
       uninstalling_registrations =
-          registry()->GetUninstallingRegistrationsForStorageKey(key);
+          registry().GetUninstallingRegistrationsForStorageKey(key);
   for (const auto& uninstalling_registration : uninstalling_registrations) {
     job_coordinator_->Abort(uninstalling_registration->scope(), key);
     uninstalling_registration->DeleteAndClearImmediately();
@@ -1069,7 +1069,7 @@
     }
   }
 
-  registry()->DeleteAndStartOver(std::move(callback));
+  registry().DeleteAndStartOver(std::move(callback));
 }
 
 void ServiceWorkerContextCore::ClearAllServiceWorkersForTest(
@@ -1082,7 +1082,7 @@
     return;
   }
   was_service_worker_registered_ = false;
-  registry()->GetAllRegistrationsInfos(
+  registry().GetAllRegistrationsInfos(
       base::BindOnce(&ClearAllServiceWorkersHelper::DidGetAllRegistrations,
                      helper, AsWeakPtr()));
 }
@@ -1091,7 +1091,7 @@
     const GURL& url,
     const blink::StorageKey& key,
     ServiceWorkerContext::CheckHasServiceWorkerCallback callback) {
-  registry()->FindRegistrationForClientUrl(
+  registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation, url, key,
       base::BindOnce(&ServiceWorkerContextCore::
                          DidFindRegistrationForCheckHasServiceWorker,
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index 0d05dd7..3d0d86a3 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -386,7 +386,7 @@
                               const GURL& source_url) override;
 
   ServiceWorkerContextWrapper* wrapper() const { return wrapper_; }
-  ServiceWorkerRegistry* registry() { return &registry_; }
+  ServiceWorkerRegistry& registry() { return registry_; }
   mojo::Remote<storage::mojom::ServiceWorkerStorageControl>&
   GetStorageControl();
   ServiceWorkerProcessManager* process_manager();
diff --git a/content/browser/service_worker/service_worker_context_core_unittest.cc b/content/browser/service_worker/service_worker_context_core_unittest.cc
index 4db6975..fcbd8b4e 100644
--- a/content/browser/service_worker/service_worker_context_core_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_core_unittest.cc
@@ -100,7 +100,7 @@
       const blink::StorageKey& key) {
     base::RunLoop loop;
     blink::ServiceWorkerStatusCode status;
-    context()->registry()->FindRegistrationForScope(
+    context()->registry().FindRegistrationForScope(
         scope, key,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode result_status,
@@ -332,7 +332,7 @@
                }));
   // Disable storage before it finishes. This causes the Unregister job to
   // complete with an error.
-  context()->registry()->DisableStorageForTesting(base::DoNothing());
+  context()->registry().DisableStorageForTesting(base::DoNothing());
   loop.Run();
 
   // The operation should still complete.
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index c31d30e..b2e4b9e 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -686,7 +686,7 @@
   EXPECT_EQ(scope, notifications_[1].scope);
   EXPECT_EQ(registration_id, notifications_[1].registration_id);
 
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id,
       blink::StorageKey::CreateFirstParty(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
@@ -741,7 +741,7 @@
   EXPECT_EQ(scope, notifications_[0].scope);
   EXPECT_EQ(registration_id, notifications_[0].registration_id);
 
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id,
       blink::StorageKey::CreateFirstParty(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
@@ -797,7 +797,7 @@
   EXPECT_EQ(scope, notifications_[1].scope);
   EXPECT_EQ(registration_id, notifications_[1].registration_id);
 
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id,
       blink::StorageKey::CreateFirstParty(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
@@ -836,7 +836,7 @@
   base::RunLoop().RunUntilIdle();
   ASSERT_TRUE(called);
 
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id,
       blink::StorageKey::CreateFirstParty(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
@@ -953,23 +953,23 @@
   base::RunLoop().RunUntilIdle();
   ASSERT_TRUE(called);
 
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id1, key1,
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, false /* expect_active */));
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id2, key1,
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, false /* expect_active */));
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id3, key2,
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
 
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id4, key3,
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
@@ -1229,7 +1229,7 @@
   content::RunAllTasksUntilIdle();
   EXPECT_TRUE(called);
 
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id, key,
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
@@ -1247,7 +1247,7 @@
 
   // The storage is disabled while the recovery process is running, so the
   // operation should be aborted.
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id, key,
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorAbort,
@@ -1256,7 +1256,7 @@
 
   // The context started over and the storage was re-initialized, so the
   // registration should not be found.
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id, key,
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
@@ -1297,7 +1297,7 @@
   content::RunAllTasksUntilIdle();
   EXPECT_TRUE(called);
 
-  context()->registry()->FindRegistrationForId(
+  context()->registry().FindRegistrationForId(
       registration_id, key,
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index bc49553..8f13f7d5 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -80,9 +80,13 @@
 
 namespace {
 
-BASE_FEATURE(kBackgroundUpdateForRegisteredStorageKeys,
-             "BackgroundUpdateForRegisteredStorageKeys",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+// Another switch for `kServiceWorkerBackgroundUpdateForRegisteredStorageKeys`
+// intended to be controlled from Field Trial (e.g. kill-switch). The original
+// flag may be overridden by `AwFieldTrials::RegisterFeatureOverrides`.
+BASE_FEATURE(
+    kServiceWorkerBackgroundUpdateForRegisteredStorageKeysFieldTrialControlled,
+    "ServiceWorkerBackgroundUpdateForRegisteredStorageKeysFieldTrialControlled",
+    base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Translate a ServiceWorkerVersion::Status to a
 // ServiceWorkerRunningInfo::ServiceWorkerVersionStatus.
@@ -274,7 +278,10 @@
       process_manager_(std::make_unique<ServiceWorkerProcessManager>()),
       storage_shared_buffer_(
           base::FeatureList::IsEnabled(
-              kBackgroundUpdateForRegisteredStorageKeys)
+              features::
+                  kServiceWorkerBackgroundUpdateForRegisteredStorageKeys) &&
+                  base::FeatureList::IsEnabled(
+                      kServiceWorkerBackgroundUpdateForRegisteredStorageKeysFieldTrialControlled)
               ? base::MakeRefCounted<
                     storage::ServiceWorkerStorage::StorageSharedBuffer>(
                     /*enable_registered_storage_keys=*/true)
@@ -754,7 +761,7 @@
         base::BindOnce(std::move(callback), std::vector<StorageUsageInfo>()));
     return;
   }
-  context()->registry()->GetAllRegistrationsInfos(base::BindOnce(
+  context()->registry().GetAllRegistrationsInfos(base::BindOnce(
       &ServiceWorkerContextWrapper::DidGetAllRegistrationsForGetAllStorageKeys,
       this, std::move(callback)));
 }
@@ -973,7 +980,7 @@
   TRACE_EVENT1("ServiceWorker", "StartServiceWorkerForNavigationHint",
                "document_url", document_url.spec());
 
-  context_core_->registry()->FindRegistrationForClientUrl(
+  context_core_->registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation,
       net::SimplifyUrlForRequest(document_url), key,
       base::BindOnce(
@@ -1186,7 +1193,7 @@
                             nullptr);
     return;
   }
-  context_core_->registry()->FindRegistrationForClientUrl(
+  context_core_->registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation,
       net::SimplifyUrlForRequest(client_url), key,
       base::BindOnce(
@@ -1205,7 +1212,7 @@
     return;
   }
   const bool include_installing_version = false;
-  context_core_->registry()->FindRegistrationForScope(
+  context_core_->registry().FindRegistrationForScope(
       net::SimplifyUrlForRequest(scope), key,
       base::BindOnce(
           &ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
@@ -1232,7 +1239,7 @@
                             nullptr);
     return;
   }
-  context_core_->registry()->FindRegistrationForId(
+  context_core_->registry().FindRegistrationForId(
       registration_id, key,
       base::BindOnce(
           &ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
@@ -1248,7 +1255,7 @@
                             nullptr);
     return;
   }
-  context_core_->registry()->FindRegistrationForIdOnly(
+  context_core_->registry().FindRegistrationForIdOnly(
       registration_id,
       base::BindOnce(
           &ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
@@ -1266,7 +1273,7 @@
                        std::vector<ServiceWorkerRegistrationInfo>()));
     return;
   }
-  context_core_->registry()->GetAllRegistrationsInfos(std::move(callback));
+  context_core_->registry().GetAllRegistrationsInfos(std::move(callback));
 }
 
 void ServiceWorkerContextWrapper::GetRegistrationsForStorageKey(
@@ -1281,8 +1288,8 @@
             std::vector<scoped_refptr<ServiceWorkerRegistration>>()));
     return;
   }
-  context_core_->registry()->GetRegistrationsForStorageKey(key,
-                                                           std::move(callback));
+  context_core_->registry().GetRegistrationsForStorageKey(key,
+                                                          std::move(callback));
 }
 
 void ServiceWorkerContextWrapper::GetRegistrationUserData(
@@ -1298,8 +1305,8 @@
                        blink::ServiceWorkerStatusCode::kErrorAbort));
     return;
   }
-  context_core_->registry()->GetUserData(registration_id, keys,
-                                         std::move(callback));
+  context_core_->registry().GetUserData(registration_id, keys,
+                                        std::move(callback));
 }
 
 void ServiceWorkerContextWrapper::GetRegistrationUserDataByKeyPrefix(
@@ -1315,8 +1322,8 @@
                        blink::ServiceWorkerStatusCode::kErrorAbort));
     return;
   }
-  context_core_->registry()->GetUserDataByKeyPrefix(registration_id, key_prefix,
-                                                    std::move(callback));
+  context_core_->registry().GetUserDataByKeyPrefix(registration_id, key_prefix,
+                                                   std::move(callback));
 }
 
 void ServiceWorkerContextWrapper::GetRegistrationUserKeysAndDataByKeyPrefix(
@@ -1332,7 +1339,7 @@
                                   base::flat_map<std::string, std::string>()));
     return;
   }
-  context_core_->registry()->GetUserKeysAndDataByKeyPrefix(
+  context_core_->registry().GetUserKeysAndDataByKeyPrefix(
       registration_id, key_prefix, std::move(callback));
 }
 
@@ -1349,8 +1356,8 @@
                                   blink::ServiceWorkerStatusCode::kErrorAbort));
     return;
   }
-  context_core_->registry()->StoreUserData(
-      registration_id, key, key_value_pairs, std::move(callback));
+  context_core_->registry().StoreUserData(registration_id, key, key_value_pairs,
+                                          std::move(callback));
 }
 
 void ServiceWorkerContextWrapper::ClearRegistrationUserData(
@@ -1366,8 +1373,8 @@
                                   blink::ServiceWorkerStatusCode::kErrorAbort));
     return;
   }
-  context_core_->registry()->ClearUserData(registration_id, keys,
-                                           std::move(callback));
+  context_core_->registry().ClearUserData(registration_id, keys,
+                                          std::move(callback));
 }
 
 void ServiceWorkerContextWrapper::ClearRegistrationUserDataByKeyPrefixes(
@@ -1382,7 +1389,7 @@
                                   blink::ServiceWorkerStatusCode::kErrorAbort));
     return;
   }
-  context_core_->registry()->ClearUserDataByKeyPrefixes(
+  context_core_->registry().ClearUserDataByKeyPrefixes(
       registration_id, key_prefixes, std::move(callback));
 }
 
@@ -1397,8 +1404,8 @@
                        blink::ServiceWorkerStatusCode::kErrorAbort));
     return;
   }
-  context_core_->registry()->GetUserDataForAllRegistrations(
-      key, std::move(callback));
+  context_core_->registry().GetUserDataForAllRegistrations(key,
+                                                           std::move(callback));
 }
 
 void ServiceWorkerContextWrapper::GetUserDataForAllRegistrationsByKeyPrefix(
@@ -1416,7 +1423,7 @@
     return;
   }
 
-  context_core_->registry()->GetUserDataForAllRegistrationsByKeyPrefix(
+  context_core_->registry().GetUserDataForAllRegistrationsByKeyPrefix(
       key_prefix, std::move(callback));
 }
 
@@ -1432,7 +1439,7 @@
                                   blink::ServiceWorkerStatusCode::kErrorAbort));
     return;
   }
-  context_core_->registry()->ClearUserDataForAllRegistrationsByKeyPrefix(
+  context_core_->registry().ClearUserDataForAllRegistrationsByKeyPrefix(
       key_prefix, std::move(callback));
 }
 
@@ -1447,7 +1454,7 @@
                                   blink::ServiceWorkerStatusCode::kErrorAbort));
     return;
   }
-  context_core_->registry()->FindRegistrationForScope(
+  context_core_->registry().FindRegistrationForScope(
       net::SimplifyUrlForRequest(scope), key,
       base::BindOnce(&DidFindRegistrationForStartActiveWorker,
                      std::move(callback)));
@@ -1459,7 +1466,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!context_core_)
     return;
-  context_core_->registry()->FindRegistrationForScope(
+  context_core_->registry().FindRegistrationForScope(
       net::SimplifyUrlForRequest(scope), key,
       base::BindOnce([](blink::ServiceWorkerStatusCode status,
                         scoped_refptr<ServiceWorkerRegistration> registration) {
@@ -1478,7 +1485,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!context_core_)
     return;
-  context_core_->registry()->FindRegistrationForScope(
+  context_core_->registry().FindRegistrationForScope(
       net::SimplifyUrlForRequest(scope), key,
       base::BindOnce(&ServiceWorkerContextWrapper::DidFindRegistrationForUpdate,
                      this));
@@ -1529,7 +1536,7 @@
                             nullptr);
     return;
   }
-  context_core_->registry()->FindRegistrationForScope(
+  context_core_->registry().FindRegistrationForScope(
       net::SimplifyUrlForRequest(scope), key,
       base::BindOnce(
           &ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
@@ -1559,7 +1566,7 @@
                "ServiceWorkerContextWrapper::MaybeProcessPendingWarmUpRequest",
                "document_url", document_url.spec());
 
-  context_core_->registry()->FindRegistrationForClientUrl(
+  context_core_->registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation,
       net::SimplifyUrlForRequest(document_url), key,
       base::BindOnce(&ServiceWorkerContextWrapper::DidFindRegistrationForWarmUp,
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index 86a9488..d0e8d44 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -443,10 +443,6 @@
   // Sets a callback to bind ServiceWorkerStorageControl for testing.
   void SetStorageControlBinderForTest(StorageControlBinder binder);
 
-  ServiceWorkerContextCore* GetContextCoreForTest() {
-    return context_core_.get();
-  }
-
   void SetForceUpdateOnPageLoadForTesting(
       bool force_update_on_page_load) override;
 
diff --git a/content/browser/service_worker/service_worker_context_wrapper_unittest.cc b/content/browser/service_worker/service_worker_context_wrapper_unittest.cc
index 7d1b685..c6ac09d 100644
--- a/content/browser/service_worker/service_worker_context_wrapper_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper_unittest.cc
@@ -75,13 +75,13 @@
   }
 
   ServiceWorkerContextCore* context() { return wrapper_->context(); }
-  ServiceWorkerRegistry* registry() { return context()->registry(); }
+  ServiceWorkerRegistry& registry() { return context()->registry(); }
 
   blink::ServiceWorkerStatusCode StoreRegistration(
       scoped_refptr<ServiceWorkerRegistration> registration) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
-    registry()->StoreRegistration(
+    registry().StoreRegistration(
         registration.get(), registration->waiting_version(),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
@@ -95,7 +95,7 @@
       scoped_refptr<ServiceWorkerRegistration> registration) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
-    registry()->DeleteRegistration(
+    registry().DeleteRegistration(
         registration,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
@@ -136,7 +136,7 @@
 
   // Store it.
   base::RunLoop loop;
-  registry()->StoreRegistration(
+  registry().StoreRegistration(
       registration.get(), registration->waiting_version(),
       base::BindLambdaForTesting(
           [&loop](blink::ServiceWorkerStatusCode status) {
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index a63bd4c..0a045bc 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -194,7 +194,7 @@
       tentative_resource_request.url.spec());
 
   // Look up a registration.
-  context_->registry()->FindRegistrationForClientUrl(
+  context_->registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNavigation,
       service_worker_client_->url(), service_worker_client_->key(),
       base::BindOnce(
@@ -548,7 +548,7 @@
       !original_registration->installing_version()) {
     // Update failed. Look up the registration again since the original
     // registration was possibly unregistered in the meantime.
-    context_->registry()->FindRegistrationForClientUrl(
+    context_->registry().FindRegistrationForClientUrl(
         ServiceWorkerRegistry::Purpose::kNotForNavigation,
         service_worker_client_->url(), service_worker_client_->key(),
         base::BindOnce(
@@ -606,7 +606,7 @@
     // When the status is REDUNDANT, the update failed (eg: script error), we
     // continue with the incumbent version.
     // In case unregister job may have run, look up the registration again.
-    context_->registry()->FindRegistrationForClientUrl(
+    context_->registry().FindRegistrationForClientUrl(
         ServiceWorkerRegistry::Purpose::kNotForNavigation,
         service_worker_client_->url(), service_worker_client_->key(),
         base::BindOnce(
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
index aac71b3..3313dd5 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
@@ -261,7 +261,7 @@
   registration_->SetActiveVersion(version_);
   {
     base::RunLoop loop;
-    context()->registry()->StoreRegistration(
+    context()->registry().StoreRegistration(
         registration_.get(), version_.get(),
         base::BindOnce(
             [](base::OnceClosure closure,
@@ -307,7 +307,7 @@
 
 TEST_F(ServiceWorkerControlleeRequestHandlerTest, Error) {
   // Disabling the storage makes looking up the registration return an error.
-  context()->registry()->DisableStorageForTesting(base::DoNothing());
+  context()->registry().DisableStorageForTesting(base::DoNothing());
 
   // Conduct a main resource load.
   ServiceWorkerRequestTestResources test_resources(
@@ -334,7 +334,7 @@
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
   base::RunLoop loop;
-  context()->registry()->StoreRegistration(
+  context()->registry().StoreRegistration(
       registration_.get(), version_.get(),
       base::BindLambdaForTesting(
           [&loop](blink::ServiceWorkerStatusCode status) { loop.Quit(); }));
@@ -366,7 +366,7 @@
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
   base::RunLoop loop;
-  context()->registry()->StoreRegistration(
+  context()->registry().StoreRegistration(
       registration_.get(), version_.get(),
       base::BindLambdaForTesting(
           [&loop](blink::ServiceWorkerStatusCode status) { loop.Quit(); }));
@@ -393,7 +393,7 @@
   version_->SetStatus(ServiceWorkerVersion::INSTALLED);
   registration_->SetWaitingVersion(version_);
   base::RunLoop loop;
-  context()->registry()->StoreRegistration(
+  context()->registry().StoreRegistration(
       registration_.get(), version_.get(),
       base::BindLambdaForTesting(
           [&loop](blink::ServiceWorkerStatusCode status) { loop.Quit(); }));
@@ -423,7 +423,7 @@
   version_->set_fetch_handler_type(
       ServiceWorkerVersion::FetchHandlerType::kNotSkippable);
   registration_->SetInstallingVersion(version_);
-  context()->registry()->NotifyInstallingRegistration(registration_.get());
+  context()->registry().NotifyInstallingRegistration(registration_.get());
 
   // Conduct a main resource load.
   ServiceWorkerRequestTestResources test_resources(
@@ -452,7 +452,7 @@
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
   base::RunLoop loop;
-  context()->registry()->StoreRegistration(
+  context()->registry().StoreRegistration(
       registration_.get(), version_.get(),
       base::BindLambdaForTesting(
           [&loop](blink::ServiceWorkerStatusCode status) { loop.Quit(); }));
@@ -482,7 +482,7 @@
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
   base::RunLoop loop;
-  context()->registry()->StoreRegistration(
+  context()->registry().StoreRegistration(
       registration_.get(), version_.get(),
       base::BindLambdaForTesting(
           [&loop](blink::ServiceWorkerStatusCode status) { loop.Quit(); }));
@@ -523,7 +523,7 @@
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
   base::RunLoop loop;
-  context()->registry()->StoreRegistration(
+  context()->registry().StoreRegistration(
       registration_.get(), version_.get(),
       base::BindLambdaForTesting(
           [&loop](blink::ServiceWorkerStatusCode status) { loop.Quit(); }));
@@ -569,7 +569,7 @@
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
   base::RunLoop loop;
-  context()->registry()->StoreRegistration(
+  context()->registry().StoreRegistration(
       registration_.get(), version_.get(),
       base::BindLambdaForTesting(
           [&loop](blink::ServiceWorkerStatusCode status) { loop.Quit(); }));
@@ -594,7 +594,7 @@
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
   base::RunLoop loop;
-  context()->registry()->StoreRegistration(
+  context()->registry().StoreRegistration(
       registration_.get(), version_.get(),
       base::BindLambdaForTesting(
           [&loop](blink::ServiceWorkerStatusCode status) { loop.Quit(); }));
diff --git a/content/browser/service_worker/service_worker_device_delegate_observer_unittest.cc b/content/browser/service_worker/service_worker_device_delegate_observer_unittest.cc
index e78af3ea..cc8d0acc 100644
--- a/content/browser/service_worker/service_worker_device_delegate_observer_unittest.cc
+++ b/content/browser/service_worker/service_worker_device_delegate_observer_unittest.cc
@@ -123,8 +123,8 @@
 void ServiceWorkerDeviceDelegateObserverTest::StoreRegistration(
     const RegistrationAndVersionPair& pair) {
   TestFuture<blink::ServiceWorkerStatusCode> status;
-  registry()->StoreRegistration(pair.first.get(), pair.second.get(),
-                                status.GetCallback());
+  registry().StoreRegistration(pair.first.get(), pair.second.get(),
+                               status.GetCallback());
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, status.Get());
 }
 
@@ -189,7 +189,7 @@
   EXPECT_FALSE(context()->GetLiveRegistration(registration_id));
   base::test::TestFuture<storage::mojom::ServiceWorkerDatabaseStatus>
       delete_future;
-  registry()->GetRemoteStorageControl()->Delete(delete_future.GetCallback());
+  registry().GetRemoteStorageControl()->Delete(delete_future.GetCallback());
   EXPECT_EQ(delete_future.Take(),
             storage::mojom::ServiceWorkerDatabaseStatus::kOk);
 
diff --git a/content/browser/service_worker/service_worker_device_delegate_observer_unittest.h b/content/browser/service_worker/service_worker_device_delegate_observer_unittest.h
index e0a5c2c..7b22270 100644
--- a/content/browser/service_worker/service_worker_device_delegate_observer_unittest.h
+++ b/content/browser/service_worker/service_worker_device_delegate_observer_unittest.h
@@ -36,7 +36,7 @@
 
   EmbeddedWorkerTestHelper* helper() { return helper_.get(); }
   ServiceWorkerContextCore* context() { return helper_->context(); }
-  ServiceWorkerRegistry* registry() { return context()->registry(); }
+  ServiceWorkerRegistry& registry() { return context()->registry(); }
 
  private:
   void InitializeTestHelper();
diff --git a/content/browser/service_worker/service_worker_hid_delegate_observer_unittest.cc b/content/browser/service_worker/service_worker_hid_delegate_observer_unittest.cc
index ed9722e..d4abc57a 100644
--- a/content/browser/service_worker/service_worker_hid_delegate_observer_unittest.cc
+++ b/content/browser/service_worker/service_worker_hid_delegate_observer_unittest.cc
@@ -288,8 +288,8 @@
     TestFuture<blink::ServiceWorkerStatusCode,
                scoped_refptr<ServiceWorkerRegistration>>
         future;
-    registry()->FindRegistrationForId(registration_id, key,
-                                      future.GetCallback());
+    registry().FindRegistrationForId(registration_id, key,
+                                     future.GetCallback());
     return future.Take();
   }
 
diff --git a/content/browser/service_worker/service_worker_installed_scripts_sender.cc b/content/browser/service_worker/service_worker_installed_scripts_sender.cc
index 817934f..350e507 100644
--- a/content/browser/service_worker/service_worker_installed_scripts_sender.cc
+++ b/content/browser/service_worker/service_worker_installed_scripts_sender.cc
@@ -84,11 +84,8 @@
   current_sending_url_ = script_url;
 
   mojo::Remote<storage::mojom::ServiceWorkerResourceReader> resource_reader;
-  owner_->context()
-      ->registry()
-      ->GetRemoteStorageControl()
-      ->CreateResourceReader(resource_id,
-                             resource_reader.BindNewPipeAndPassReceiver());
+  owner_->context()->registry().GetRemoteStorageControl()->CreateResourceReader(
+      resource_id, resource_reader.BindNewPipeAndPassReceiver());
   TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("ServiceWorker", "SendingScript", this,
                                     "script_url", current_sending_url_.spec());
   reader_ = std::make_unique<ServiceWorkerInstalledScriptReader>(
diff --git a/content/browser/service_worker/service_worker_installed_scripts_sender_unittest.cc b/content/browser/service_worker/service_worker_installed_scripts_sender_unittest.cc
index 74801d4..4e34e211 100644
--- a/content/browser/service_worker/service_worker_installed_scripts_sender_unittest.cc
+++ b/content/browser/service_worker/service_worker_installed_scripts_sender_unittest.cc
@@ -154,7 +154,7 @@
 
   EmbeddedWorkerTestHelper* helper() { return helper_.get(); }
   ServiceWorkerContextCore* context() { return helper_->context(); }
-  ServiceWorkerRegistry* registry() { return context()->registry(); }
+  ServiceWorkerRegistry& registry() { return context()->registry(); }
   ServiceWorkerVersion* version() { return version_.get(); }
 
  private:
@@ -707,7 +707,7 @@
   version()->script_cache_map()->SetResources(records);
 
   base::RunLoop loop;
-  registry()->DisableStorageForTesting(loop.QuitClosure());
+  registry().DisableStorageForTesting(loop.QuitClosure());
   loop.Run();
 
   auto sender =
diff --git a/content/browser/service_worker/service_worker_internals_ui_browsertest.cc b/content/browser/service_worker/service_worker_internals_ui_browsertest.cc
index b6f02714..0c06fba 100644
--- a/content/browser/service_worker/service_worker_internals_ui_browsertest.cc
+++ b/content/browser/service_worker/service_worker_internals_ui_browsertest.cc
@@ -236,7 +236,7 @@
     wrapper()
         ->context()
         ->registry()
-        ->GetRemoteStorageControl()
+        .GetRemoteStorageControl()
         .FlushForTesting();
     content::RunAllTasksUntilIdle();
     wrapper_ = nullptr;
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index 1c3da97..af84a3a5 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -199,7 +199,7 @@
   ServiceWorkerJobCoordinator* job_coordinator() const {
     return context()->job_coordinator();
   }
-  ServiceWorkerRegistry* registry() const { return context()->registry(); }
+  ServiceWorkerRegistry& registry() const { return context()->registry(); }
 
   bool UseFirstPartyStorageKey() {
     return GetParam() == StorageKeyTestCase::kFirstParty;
@@ -309,7 +309,7 @@
     blink::ServiceWorkerStatusCode expected_status) {
   scoped_refptr<ServiceWorkerRegistration> registration;
   base::RunLoop run_loop;
-  registry()->FindRegistrationForScope(
+  registry().FindRegistrationForScope(
       scope, key,
       SaveFoundRegistration(expected_status, &registration,
                             run_loop.QuitClosure()));
@@ -378,12 +378,12 @@
   base::RunLoop run_loop;
   base::RepeatingClosure barrier_closure =
       base::BarrierClosure(2, run_loop.QuitClosure());
-  registry()->FindRegistrationForClientUrl(
+  registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation, url, key,
       SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration1,
                             barrier_closure));
   scoped_refptr<ServiceWorkerRegistration> registration2;
-  registry()->FindRegistrationForClientUrl(
+  registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation, url, key,
       SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration2,
                             barrier_closure));
@@ -409,14 +409,14 @@
   base::RunLoop run_loop;
   base::RepeatingClosure barrier_closure =
       base::BarrierClosure(2, run_loop.QuitClosure());
-  registry()->FindRegistrationForClientUrl(
+  registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation,
       GURL("https://www.example.com/one"), key,
       SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration1,
                             barrier_closure));
 
   scoped_refptr<ServiceWorkerRegistration> registration2;
-  registry()->FindRegistrationForClientUrl(
+  registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation,
       GURL("https://www.example.com/two"), key,
       SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration2,
@@ -443,12 +443,12 @@
   base::RunLoop run_loop;
   base::RepeatingClosure barrier_closure =
       base::BarrierClosure(2, run_loop.QuitClosure());
-  registry()->FindRegistrationForClientUrl(
+  registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation, scope1, key,
       SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration1,
                             barrier_closure));
   scoped_refptr<ServiceWorkerRegistration> registration2;
-  registry()->FindRegistrationForClientUrl(
+  registry().FindRegistrationForClientUrl(
       ServiceWorkerRegistry::Purpose::kNotForNavigation, scope2, key,
       SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration2,
                             barrier_closure));
@@ -536,7 +536,7 @@
   RunUnregisterJob(options.scope, key);
 
   WaitForVersionRunningStatus(version, blink::EmbeddedWorkerStatus::kStopped);
-  registry()->GetRemoteStorageControl().FlushForTesting();
+  registry().GetRemoteStorageControl().FlushForTesting();
   base::RunLoop().RunUntilIdle();
 
   // The service worker registration object host and service worker object host
diff --git a/content/browser/service_worker/service_worker_main_resource_loader_unittest.cc b/content/browser/service_worker/service_worker_main_resource_loader_unittest.cc
index c1f0b84..443f628 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader_unittest.cc
+++ b/content/browser/service_worker/service_worker_main_resource_loader_unittest.cc
@@ -518,7 +518,7 @@
     registration_->set_last_update_check(base::Time::Now());
     std::optional<blink::ServiceWorkerStatusCode> status;
     base::RunLoop run_loop;
-    registry()->StoreRegistration(
+    registry().StoreRegistration(
         registration_.get(), version_.get(),
         ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure()));
     run_loop.Run();
@@ -548,7 +548,7 @@
     }
   }
 
-  ServiceWorkerRegistry* registry() { return helper_->context()->registry(); }
+  ServiceWorkerRegistry& registry() { return helper_->context()->registry(); }
   mojo::Remote<storage::mojom::ServiceWorkerStorageControl>&
   GetStorageControl() {
     return helper_->context()->GetStorageControl();
diff --git a/content/browser/service_worker/service_worker_new_script_loader.cc b/content/browser/service_worker/service_worker_new_script_loader.cc
index dbb5b24..da38e60 100644
--- a/content/browser/service_worker/service_worker_new_script_loader.cc
+++ b/content/browser/service_worker/service_worker_new_script_loader.cc
@@ -156,7 +156,7 @@
   mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer;
   version_->context()
       ->registry()
-      ->GetRemoteStorageControl()
+      .GetRemoteStorageControl()
       ->CreateResourceWriter(cache_resource_id,
                              writer.BindNewPipeAndPassReceiver());
 
diff --git a/content/browser/service_worker/service_worker_object_host_unittest.cc b/content/browser/service_worker/service_worker_object_host_unittest.cc
index b66995f..08ada3bb 100644
--- a/content/browser/service_worker/service_worker_object_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_object_host_unittest.cc
@@ -145,7 +145,7 @@
     // Make the registration findable via storage functions.
     std::optional<blink::ServiceWorkerStatusCode> status;
     base::RunLoop run_loop;
-    helper_->context()->registry()->StoreRegistration(
+    helper_->context()->registry().StoreRegistration(
         registration_.get(), version_.get(),
         ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure()));
     run_loop.Run();
diff --git a/content/browser/service_worker/service_worker_quota_client.cc b/content/browser/service_worker/service_worker_quota_client.cc
index afd55e7..31d32039 100644
--- a/content/browser/service_worker/service_worker_quota_client.cc
+++ b/content/browser/service_worker/service_worker_quota_client.cc
@@ -56,7 +56,7 @@
     std::move(callback).Run(0);
     return;
   }
-  context_->registry()->GetStorageUsageForStorageKey(
+  context_->registry().GetStorageUsageForStorageKey(
       bucket.storage_key,
       base::BindOnce(&FindUsageForStorageKey, std::move(callback)));
 }
@@ -64,7 +64,7 @@
 void ServiceWorkerQuotaClient::GetDefaultStorageKeys(
     GetDefaultStorageKeysCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  context_->registry()->GetRegisteredStorageKeys(std::move(callback));
+  context_->registry().GetRegisteredStorageKeys(std::move(callback));
 }
 
 void ServiceWorkerQuotaClient::DeleteBucketData(
@@ -87,7 +87,7 @@
 void ServiceWorkerQuotaClient::PerformStorageCleanup(
     PerformStorageCleanupCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  context_->registry()->PerformStorageCleanup(std::move(callback));
+  context_->registry().PerformStorageCleanup(std::move(callback));
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc
index e4471d7..97a71db 100644
--- a/content/browser/service_worker/service_worker_register_job.cc
+++ b/content/browser/service_worker/service_worker_register_job.cc
@@ -163,15 +163,15 @@
   }
 
   scoped_refptr<ServiceWorkerRegistration> registration =
-      context_->registry()->GetUninstallingRegistration(scope_, key_);
+      context_->registry().GetUninstallingRegistration(scope_, key_);
   if (registration.get())
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
         FROM_HERE,
         base::BindOnce(std::move(next_step),
                        blink::ServiceWorkerStatusCode::kOk, registration));
   else
-    context_->registry()->FindRegistrationForScope(scope_, key_,
-                                                   std::move(next_step));
+    context_->registry().FindRegistrationForScope(scope_, key_,
+                                                  std::move(next_step));
 }
 
 void ServiceWorkerRegisterJob::Abort() {
@@ -393,7 +393,7 @@
       // Update resource list on the database. Pass a no-op callback as the
       // checksums are only used for an optimization and we don't need to wait
       // for the completion.
-      context_->registry()->UpdateResourceSha256Checksums(
+      context_->registry().UpdateResourceSha256Checksums(
           registration()->id(), key_, updated_checksum_map,
           base::BindOnce([](blink::ServiceWorkerStatusCode status) {
             UMA_HISTOGRAM_ENUMERATION(
@@ -408,8 +408,8 @@
     return;
   }
 
-  context_->registry()->NotifyInstallingRegistration(registration());
-  context_->registry()->CreateNewVersion(
+  context_->registry().NotifyInstallingRegistration(registration());
+  context_->registry().CreateNewVersion(
       registration(), script_url_, worker_script_type_,
       base::BindOnce(&ServiceWorkerRegisterJob::StartWorkerForUpdate,
                      weak_factory_.GetWeakPtr()));
@@ -421,7 +421,7 @@
 
   blink::mojom::ServiceWorkerRegistrationOptions options(
       scope_, worker_script_type_, update_via_cache_);
-  context_->registry()->CreateNewRegistration(
+  context_->registry().CreateNewRegistration(
       options, key_, ancestor_frame_type_,
       base::BindOnce(&ServiceWorkerRegisterJob::ContinueWithNewRegistration,
                      weak_factory_.GetWeakPtr()));
@@ -636,14 +636,13 @@
   }
 
   if (!IsUpdateCheckNeeded()) {
-    context_->registry()->NotifyInstallingRegistration(registration());
+    context_->registry().NotifyInstallingRegistration(registration());
     base::OnceCallback<void(scoped_refptr<ServiceWorkerVersion>)> next_task =
         base::BindOnce(&ServiceWorkerRegisterJob::
                            MaybeThrottleForDevToolsBeforeStartingScriptFetch,
                        weak_factory_.GetWeakPtr());
-    context_->registry()->CreateNewVersion(
-        registration(), script_url_, worker_script_type_,
-        std::move(next_task));
+    context_->registry().CreateNewVersion(
+        registration(), script_url_, worker_script_type_, std::move(next_task));
     return;
   }
 
@@ -770,7 +769,7 @@
 
   SetPhase(STORE);
   DCHECK(!registration()->last_update_check().is_null());
-  context_->registry()->StoreRegistration(
+  context_->registry().StoreRegistration(
       registration(), new_version(),
       base::BindOnce(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete,
                      weak_factory_.GetWeakPtr()));
@@ -865,9 +864,9 @@
       if (!registration()->newest_installed_version()) {
         registration()->NotifyRegistrationFailed();
         if (!registration()->is_deleted()) {
-          context_->registry()->DeleteRegistration(registration(),
-                                                   base::DoNothing());
-          context_->registry()->NotifyDoneUninstallingRegistration(
+          context_->registry().DeleteRegistration(registration(),
+                                                  base::DoNothing());
+          context_->registry().NotifyDoneUninstallingRegistration(
               registration(), ServiceWorkerRegistration::Status::kUninstalled);
         }
       }
@@ -877,7 +876,7 @@
   }
   DCHECK(callbacks_.empty());
   if (registration()) {
-    context_->registry()->NotifyDoneInstallingRegistration(
+    context_->registry().NotifyDoneInstallingRegistration(
         registration(), new_version(), status);
 #if DCHECK_IS_ON()
     switch (registration()->status()) {
@@ -965,7 +964,7 @@
     registration()->set_last_update_check(base::Time::Now());
 
     if (registration()->newest_installed_version()) {
-      context_->registry()->UpdateLastUpdateCheckTime(
+      context_->registry().UpdateLastUpdateCheckTime(
           registration()->id(), registration()->key(),
           registration()->last_update_check(),
           base::BindOnce([](blink::ServiceWorkerStatusCode status) {
diff --git a/content/browser/service_worker/service_worker_registration.cc b/content/browser/service_worker/service_worker_registration.cc
index 79dff88..6f065a1c 100644
--- a/content/browser/service_worker/service_worker_registration.cc
+++ b/content/browser/service_worker/service_worker_registration.cc
@@ -362,7 +362,7 @@
     return;
   }
 
-  context_->registry()->DeleteRegistration(
+  context_->registry().DeleteRegistration(
       this, base::BindOnce(&ServiceWorkerRegistration::OnDeleteFinished, this));
 
   if (!active_version() || !active_version()->HasControllee())
@@ -372,7 +372,7 @@
 void ServiceWorkerRegistration::DeleteAndClearImmediately() {
   DCHECK(context_);
   if (!is_deleted()) {
-    context_->registry()->DeleteRegistration(
+    context_->registry().DeleteRegistration(
         this,
         base::BindOnce(&ServiceWorkerRegistration::OnDeleteFinished, this));
   }
@@ -395,14 +395,14 @@
           << "attempt to resurrect a completely uninstalled registration";
   }
 
-  context_->registry()->NotifyDoneUninstallingRegistration(this,
-                                                           Status::kIntact);
+  context_->registry().NotifyDoneUninstallingRegistration(this,
+                                                          Status::kIntact);
 
   scoped_refptr<ServiceWorkerVersion> most_recent_version =
       waiting_version() ? waiting_version() : active_version();
   DCHECK(most_recent_version.get());
-  context_->registry()->NotifyInstallingRegistration(this);
-  context_->registry()->StoreRegistration(
+  context_->registry().NotifyInstallingRegistration(this);
+  context_->registry().StoreRegistration(
       this, most_recent_version.get(),
       base::BindOnce(&ServiceWorkerRegistration::OnRestoreFinished, this,
                      std::move(callback), most_recent_version));
@@ -613,13 +613,13 @@
 
   // Delete the registration and its state from storage.
   if (status() == Status::kIntact) {
-    context_->registry()->DeleteRegistration(
+    context_->registry().DeleteRegistration(
         this,
         base::BindOnce(&ServiceWorkerRegistration::OnDeleteFinished, protect));
   }
   DCHECK(is_uninstalling());
-  context_->registry()->NotifyDoneUninstallingRegistration(
-      this, Status::kUninstalled);
+  context_->registry().NotifyDoneUninstallingRegistration(this,
+                                                          Status::kUninstalled);
 
   // Tell observers that this registration is gone.
   NotifyRegistrationFailed();
@@ -714,7 +714,7 @@
     activating_version->router_evaluator()->RecordRouterRuleInfo();
   }
 
-  context_->registry()->UpdateToActiveState(id(), key_, base::DoNothing());
+  context_->registry().UpdateToActiveState(id(), key_, base::DoNothing());
 }
 
 void ServiceWorkerRegistration::OnDeleteFinished(
@@ -736,7 +736,7 @@
   auto protect = base::WrapRefCounted(this);
 
   if (context_) {
-    context_->registry()->NotifyDoneUninstallingRegistration(
+    context_->registry().NotifyDoneUninstallingRegistration(
         this, Status::kUninstalled);
   }
 
@@ -781,8 +781,8 @@
     std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort);
     return;
   }
-  context_->registry()->NotifyDoneInstallingRegistration(this, version.get(),
-                                                         status);
+  context_->registry().NotifyDoneInstallingRegistration(this, version.get(),
+                                                        status);
   std::move(callback).Run(status);
 }
 
diff --git a/content/browser/service_worker/service_worker_registration_object_host.cc b/content/browser/service_worker/service_worker_registration_object_host.cc
index a1dffc8..c339c2d 100644
--- a/content/browser/service_worker/service_worker_registration_object_host.cc
+++ b/content/browser/service_worker/service_worker_registration_object_host.cc
@@ -176,7 +176,7 @@
     return;
   }
 
-  context_->registry()->UpdateNavigationPreloadEnabled(
+  context_->registry().UpdateNavigationPreloadEnabled(
       registration_->id(), registration_->key(), enable,
       base::BindOnce(&ServiceWorkerRegistrationObjectHost::
                          DidUpdateNavigationPreloadEnabled,
@@ -226,7 +226,7 @@
     return;
   }
 
-  context_->registry()->UpdateNavigationPreloadHeader(
+  context_->registry().UpdateNavigationPreloadHeader(
       registration_->id(), registration_->key(), value,
       base::BindOnce(&ServiceWorkerRegistrationObjectHost::
                          DidUpdateNavigationPreloadHeader,
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index 66da5c87..7589751 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -189,7 +189,7 @@
   }
 
   ServiceWorkerContextCore* context() { return helper_->context(); }
-  ServiceWorkerRegistry* registry() { return helper_->context()->registry(); }
+  ServiceWorkerRegistry& registry() { return helper_->context()->registry(); }
 
   class RegistrationListener : public ServiceWorkerRegistration::Listener {
    public:
@@ -427,7 +427,7 @@
         EmbeddedWorkerTestHelper::CreateMainScriptResponse());
     std::optional<blink::ServiceWorkerStatusCode> status;
     base::RunLoop run_loop;
-    context()->registry()->StoreRegistration(
+    context()->registry().StoreRegistration(
         registration_.get(), version_1.get(),
         ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure()));
     run_loop.Run();
@@ -883,7 +883,7 @@
       int64_t registration_id,
       const blink::StorageKey& key) {
     std::optional<blink::ServiceWorkerStatusCode> status;
-    registry()->FindRegistrationForId(
+    registry().FindRegistrationForId(
         registration_id, key,
         base::BindOnce(
             [](std::optional<blink::ServiceWorkerStatusCode>* out_status,
@@ -936,7 +936,7 @@
     bool called = false;
     blink::ServiceWorkerStatusCode status =
         blink::ServiceWorkerStatusCode::kErrorFailed;
-    registry()->StoreRegistration(
+    registry().StoreRegistration(
         registration.get(), version.get(),
         base::BindOnce(&SaveStatusCallback, &called, &status));
     base::RunLoop().RunUntilIdle();
diff --git a/content/browser/service_worker/service_worker_registry.cc b/content/browser/service_worker/service_worker_registry.cc
index 3027502..37320d26 100644
--- a/content/browser/service_worker/service_worker_registry.cc
+++ b/content/browser/service_worker/service_worker_registry.cc
@@ -225,10 +225,10 @@
 
 ServiceWorkerRegistry::ServiceWorkerRegistry(
     ServiceWorkerContextCore& context,
-    ServiceWorkerRegistry* old_registry)
+    ServiceWorkerRegistry& old_registry)
     : ServiceWorkerRegistry(context,
-                            old_registry->quota_manager_proxy_.get(),
-                            old_registry->special_storage_policy_.get()) {}
+                            old_registry.quota_manager_proxy_.get(),
+                            old_registry.special_storage_policy_.get()) {}
 
 ServiceWorkerRegistry::~ServiceWorkerRegistry() = default;
 
diff --git a/content/browser/service_worker/service_worker_registry.h b/content/browser/service_worker/service_worker_registry.h
index 2c5e879..16c24cd2 100644
--- a/content/browser/service_worker/service_worker_registry.h
+++ b/content/browser/service_worker/service_worker_registry.h
@@ -105,7 +105,7 @@
   // For re-creating the registry from the old one. This is called when
   // something went wrong during storage access.
   ServiceWorkerRegistry(ServiceWorkerContextCore& context,
-                        ServiceWorkerRegistry* old_registry);
+                        ServiceWorkerRegistry& old_registry);
 
   ~ServiceWorkerRegistry();
 
diff --git a/content/browser/service_worker/service_worker_registry_unittest.cc b/content/browser/service_worker/service_worker_registry_unittest.cc
index 270cf03..e732ad7b 100644
--- a/content/browser/service_worker/service_worker_registry_unittest.cc
+++ b/content/browser/service_worker/service_worker_registry_unittest.cc
@@ -287,9 +287,9 @@
 
   EmbeddedWorkerTestHelper* helper() { return helper_.get(); }
   ServiceWorkerContextCore* context() { return helper_->context(); }
-  ServiceWorkerRegistry* registry() { return context()->registry(); }
+  ServiceWorkerRegistry& registry() { return context()->registry(); }
   mojo::Remote<storage::mojom::ServiceWorkerStorageControl>& storage_control() {
-    return registry()->GetRemoteStorageControl();
+    return registry().GetRemoteStorageControl();
   }
 
   storage::MockSpecialStoragePolicy* special_storage_policy() {
@@ -297,19 +297,19 @@
   }
 
   storage::QuotaManagerProxy* quota_manager_proxy() {
-    return registry()->quota_manager_proxy_.get();
+    return registry().quota_manager_proxy_.get();
   }
 
-  size_t inflight_call_count() { return registry()->inflight_calls_.size(); }
+  size_t inflight_call_count() { return registry().inflight_calls_.size(); }
 
   base::LRUCache<blink::StorageKey, std::set<GURL>>&
   registration_scope_cache() {
-    return registry()->registration_scope_cache_;
+    return registry().registration_scope_cache_;
   }
 
   std::set<blink::StorageKey> registration_scope_cache_keys() {
     std::set<blink::StorageKey> keys;
-    for (const auto& it : registry()->registration_scope_cache_) {
+    for (const auto& it : registry().registration_scope_cache_) {
       keys.insert(it.first);
     }
     return keys;
@@ -317,12 +317,12 @@
 
   base::LRUCache<std::tuple<GURL, blink::StorageKey>, int64_t>&
   registration_id_cache() {
-    return registry()->registration_id_cache_;
+    return registry().registration_id_cache_;
   }
 
   std::set<GURL> registration_id_cache_urls() {
     std::set<GURL> set;
-    for (const auto& it : registry()->registration_id_cache_) {
+    for (const auto& it : registry().registration_id_cache_) {
       set.insert(std::get<0>(it.first));
     }
     return set;
@@ -352,7 +352,7 @@
   std::vector<blink::StorageKey> GetRegisteredStorageKeys() {
     std::vector<blink::StorageKey> result;
     base::RunLoop loop;
-    registry()->GetRegisteredStorageKeys(base::BindLambdaForTesting(
+    registry().GetRegisteredStorageKeys(base::BindLambdaForTesting(
         [&](const std::vector<blink::StorageKey>& storage_keys) {
           result = storage_keys;
           loop.Quit();
@@ -367,7 +367,7 @@
       scoped_refptr<ServiceWorkerRegistration>& out_registration) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
-    registry()->FindRegistrationForClientUrl(
+    registry().FindRegistrationForClientUrl(
         ServiceWorkerRegistry::Purpose::kNotForNavigation, document_url, key,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
@@ -386,7 +386,7 @@
       scoped_refptr<ServiceWorkerRegistration>& out_registration) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
-    registry()->FindRegistrationForScope(
+    registry().FindRegistrationForScope(
         scope, key,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
@@ -405,7 +405,7 @@
       scoped_refptr<ServiceWorkerRegistration>& out_registration) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
-    registry()->FindRegistrationForId(
+    registry().FindRegistrationForId(
         registration_id, key,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
@@ -423,7 +423,7 @@
       scoped_refptr<ServiceWorkerRegistration>& out_registration) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
-    registry()->FindRegistrationForIdOnly(
+    registry().FindRegistrationForIdOnly(
         registration_id,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
@@ -441,7 +441,7 @@
       scoped_refptr<ServiceWorkerVersion> version) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
-    registry()->StoreRegistration(
+    registry().StoreRegistration(
         registration.get(), version.get(),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
@@ -455,7 +455,7 @@
       scoped_refptr<ServiceWorkerRegistration> registration) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
-    registry()->DeleteRegistration(
+    registry().DeleteRegistration(
         registration,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
@@ -469,7 +469,7 @@
       const ServiceWorkerRegistration* registration) {
     base::RunLoop loop;
     blink::ServiceWorkerStatusCode result;
-    registry()->UpdateToActiveState(
+    registry().UpdateToActiveState(
         registration->id(), registration->key(),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
@@ -483,7 +483,7 @@
       const ServiceWorkerRegistration* registration) {
     base::RunLoop loop;
     blink::ServiceWorkerStatusCode result;
-    registry()->UpdateLastUpdateCheckTime(
+    registry().UpdateLastUpdateCheckTime(
         registration->id(), registration->key(),
         registration->last_update_check(),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
@@ -499,7 +499,7 @@
       ServiceWorkerVersion::FetchHandlerType fetch_handler_type) {
     base::RunLoop loop;
     blink::ServiceWorkerStatusCode result;
-    registry()->UpdateFetchHandlerType(
+    registry().UpdateFetchHandlerType(
         registration->id(), registration->key(), fetch_handler_type,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
@@ -514,7 +514,7 @@
       const base::flat_map<int64_t, std::string>& updated_sha256_checksums) {
     base::RunLoop loop;
     blink::ServiceWorkerStatusCode result;
-    registry()->UpdateResourceSha256Checksums(
+    registry().UpdateResourceSha256Checksums(
         registration->id(), registration->key(), updated_sha256_checksums,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
@@ -528,7 +528,7 @@
       const blink::StorageKey& key) {
     GetStorageUsageForStorageKeyResult result;
     base::RunLoop loop;
-    registry()->GetStorageUsageForStorageKey(
+    registry().GetStorageUsageForStorageKey(
         key, base::BindLambdaForTesting(
                  [&](blink::ServiceWorkerStatusCode status, int64_t usage) {
                    result.status = status;
@@ -543,7 +543,7 @@
       std::vector<ServiceWorkerRegistrationInfo>* registrations) {
     std::optional<blink::ServiceWorkerStatusCode> result;
     base::RunLoop loop;
-    registry()->GetAllRegistrationsInfos(base::BindLambdaForTesting(
+    registry().GetAllRegistrationsInfos(base::BindLambdaForTesting(
         [&](blink::ServiceWorkerStatusCode status,
             const std::vector<ServiceWorkerRegistrationInfo>& infos) {
           result = status;
@@ -560,7 +560,7 @@
       std::vector<scoped_refptr<ServiceWorkerRegistration>>& registrations) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
-    registry()->GetRegistrationsForStorageKey(
+    registry().GetRegistrationsForStorageKey(
         key,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
@@ -698,7 +698,7 @@
       quota_manager_proxy());
 
   base::RunLoop loop;
-  registry()->CreateNewRegistration(
+  registry().CreateNewRegistration(
       std::move(options), kKey, blink::mojom::AncestorFrameType::kNormalFrame,
       base::BindLambdaForTesting(
           [&](scoped_refptr<ServiceWorkerRegistration> new_registration) {
@@ -732,7 +732,7 @@
       quota_manager_proxy());
 
   base::RunLoop loop;
-  registry()->CreateNewRegistration(
+  registry().CreateNewRegistration(
       std::move(options), kKey, blink::mojom::AncestorFrameType::kNormalFrame,
       base::BindLambdaForTesting(
           [&](scoped_refptr<ServiceWorkerRegistration> new_registration) {
@@ -1035,7 +1035,7 @@
   EXPECT_TRUE(registrations_for_storage_key.empty());
 
   // Notify storage of it being installed.
-  registry()->NotifyInstallingRegistration(live_registration.get());
+  registry().NotifyInstallingRegistration(live_registration.get());
 
   // Now should be findable.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
@@ -1079,7 +1079,7 @@
   EXPECT_TRUE(registrations_for_storage_key.empty());
 
   // Notify storage of installation no longer happening.
-  registry()->NotifyDoneInstallingRegistration(
+  registry().NotifyDoneInstallingRegistration(
       live_registration.get(), nullptr, blink::ServiceWorkerStatusCode::kOk);
 
   // Once again, should not be findable.
@@ -1150,9 +1150,9 @@
                                                 /*resource_id=*/3);
 
   // Notify storage of them being installed.
-  registry()->NotifyInstallingRegistration(live_registration1.get());
-  registry()->NotifyInstallingRegistration(live_registration2.get());
-  registry()->NotifyInstallingRegistration(live_registration3.get());
+  registry().NotifyInstallingRegistration(live_registration1.get());
+  registry().NotifyInstallingRegistration(live_registration2.get());
+  registry().NotifyInstallingRegistration(live_registration3.get());
 
   // Registrations in the installing state shouldn't trigger a modified
   // notification.
@@ -1180,11 +1180,11 @@
   EXPECT_EQ(3, helper()->quota_manager_proxy()->notify_bucket_modified_count());
 
   // Notify storage of installations no longer happening.
-  registry()->NotifyDoneInstallingRegistration(
+  registry().NotifyDoneInstallingRegistration(
       live_registration1.get(), nullptr, blink::ServiceWorkerStatusCode::kOk);
-  registry()->NotifyDoneInstallingRegistration(
+  registry().NotifyDoneInstallingRegistration(
       live_registration2.get(), nullptr, blink::ServiceWorkerStatusCode::kOk);
-  registry()->NotifyDoneInstallingRegistration(
+  registry().NotifyDoneInstallingRegistration(
       live_registration3.get(), nullptr, blink::ServiceWorkerStatusCode::kOk);
 
   EXPECT_EQ(0, helper()->quota_manager_proxy()->notify_bucket_accessed_count());
@@ -1234,7 +1234,7 @@
   int done_count = 0;
   base::RunLoop loop;
   for (int i = 0; i < kCallCount; i++) {
-    registry()->FindRegistrationForClientUrl(
+    registry().FindRegistrationForClientUrl(
         ServiceWorkerRegistry::Purpose::kNotForNavigation, kScope, kKey,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
@@ -1301,7 +1301,7 @@
       int expected_registration_id_cache_size,
       const base::Location& location = FROM_HERE) {
     base::RunLoop loop;
-    registry()->FindRegistrationForClientUrl(
+    registry().FindRegistrationForClientUrl(
         ServiceWorkerRegistry::Purpose::kNotForNavigation, scope,
         blink::StorageKey::CreateFirstParty(url::Origin::Create(scope)),
         base::BindLambdaForTesting(
@@ -1790,7 +1790,7 @@
   registration->EnableNavigationPreload(true);
   registration->SetNavigationPreloadHeader("header");
 
-  registry()->NotifyInstallingRegistration(registration.get());
+  registry().NotifyInstallingRegistration(registration.get());
   ASSERT_EQ(StoreRegistration(registration, registration->waiting_version()),
             blink::ServiceWorkerStatusCode::kOk);
 
@@ -2040,7 +2040,7 @@
 
   ASSERT_EQ(StoreRegistration(registration, registration->waiting_version()),
             blink::ServiceWorkerStatusCode::kOk);
-  EXPECT_FALSE(registry()->ShouldPurgeOnShutdownForTesting(kKey));
+  EXPECT_FALSE(registry().ShouldPurgeOnShutdownForTesting(kKey));
 
   {
     // Update storage policy to mark the origin should be purged on shutdown.
@@ -2049,7 +2049,7 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  EXPECT_TRUE(registry()->ShouldPurgeOnShutdownForTesting(kKey));
+  EXPECT_TRUE(registry().ShouldPurgeOnShutdownForTesting(kKey));
 }
 
 // Tests that callbacks of storage operations are always called even when the
@@ -2111,7 +2111,7 @@
     helper()->SimulateStorageRestartForTesting();
 
     base::RunLoop loop1;
-    registry()->StoreRegistration(
+    registry().StoreRegistration(
         registration1.get(), registration1->waiting_version(),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2121,7 +2121,7 @@
     helper()->SimulateStorageRestartForTesting();
 
     base::RunLoop loop2;
-    registry()->StoreRegistration(
+    registry().StoreRegistration(
         registration2.get(), registration2->waiting_version(),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2139,7 +2139,7 @@
   // Get registered storage keys.
   {
     base::RunLoop loop;
-    registry()->GetRegisteredStorageKeys(base::BindLambdaForTesting(
+    registry().GetRegisteredStorageKeys(base::BindLambdaForTesting(
         [&](const std::vector<blink::StorageKey>& storage_keys) {
           EXPECT_THAT(storage_keys,
                       testing::UnorderedElementsAreArray({kKey1, kKey2}));
@@ -2156,7 +2156,7 @@
   // Finding registrations stored in the previous block.
   {
     base::RunLoop loop1;
-    registry()->FindRegistrationForClientUrl(
+    registry().FindRegistrationForClientUrl(
         ServiceWorkerRegistry::Purpose::kNotForNavigation, kScope1, kKey1,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
@@ -2168,7 +2168,7 @@
 
     base::RunLoop loop2;
     const GURL kNotInScope("http://www.example.com/not-in-scope");
-    registry()->FindRegistrationForScope(
+    registry().FindRegistrationForScope(
         kNotInScope,
         blink::StorageKey::CreateFirstParty(url::Origin::Create(kNotInScope)),
         base::BindLambdaForTesting(
@@ -2190,7 +2190,7 @@
   // Get both of the registrations by these APIs.
   {
     base::RunLoop loop1;
-    registry()->GetRegistrationsForStorageKey(
+    registry().GetRegistrationsForStorageKey(
         kKey1,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
@@ -2202,7 +2202,7 @@
             }));
 
     base::RunLoop loop2;
-    registry()->GetAllRegistrationsInfos(base::BindLambdaForTesting(
+    registry().GetAllRegistrationsInfos(base::BindLambdaForTesting(
         [&](blink::ServiceWorkerStatusCode status,
             const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2221,7 +2221,7 @@
   // Delete `registrations` from the storage.
   {
     base::RunLoop loop;
-    registry()->DeleteRegistration(
+    registry().DeleteRegistration(
         registration2,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2238,7 +2238,7 @@
   // Update fields of `registration1` in the storage.
   {
     base::RunLoop loop1;
-    registry()->UpdateToActiveState(
+    registry().UpdateToActiveState(
         registration1->id(), kKey1,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2246,7 +2246,7 @@
         }));
 
     base::RunLoop loop2;
-    registry()->UpdateLastUpdateCheckTime(
+    registry().UpdateLastUpdateCheckTime(
         registration1->id(), kKey1, base::Time::Now(),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2254,7 +2254,7 @@
         }));
 
     base::RunLoop loop3;
-    registry()->UpdateNavigationPreloadEnabled(
+    registry().UpdateNavigationPreloadEnabled(
         registration1->id(), kKey1, /*enable=*/true,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2262,7 +2262,7 @@
         }));
 
     base::RunLoop loop4;
-    registry()->UpdateNavigationPreloadHeader(
+    registry().UpdateNavigationPreloadHeader(
         registration1->id(), kKey1, "header",
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2318,7 +2318,7 @@
   StoreRegistrationData(std::move(data2), std::move(resources2));
 
   base::RunLoop loop1;
-  registry()->FindRegistrationForId(
+  registry().FindRegistrationForId(
       registration_id1, key1,
       base::BindLambdaForTesting(
           [&](blink::ServiceWorkerStatusCode status,
@@ -2329,7 +2329,7 @@
           }));
 
   base::RunLoop loop2;
-  registry()->FindRegistrationForIdOnly(
+  registry().FindRegistrationForIdOnly(
       registration_id2,
       base::BindLambdaForTesting(
           [&](blink::ServiceWorkerStatusCode status,
@@ -2365,7 +2365,7 @@
         quota_manager_proxy());
 
     base::RunLoop loop;
-    registry()->CreateNewRegistration(
+    registry().CreateNewRegistration(
         std::move(options), kKey, blink::mojom::AncestorFrameType::kNormalFrame,
         base::BindLambdaForTesting(
             [&](scoped_refptr<ServiceWorkerRegistration> new_registration) {
@@ -2391,7 +2391,7 @@
 
   {
     base::RunLoop loop;
-    registry()->CreateNewVersion(
+    registry().CreateNewVersion(
         registration, kScriptUrl, blink::mojom::ScriptType::kClassic,
         base::BindLambdaForTesting(
             [&](scoped_refptr<ServiceWorkerVersion> new_version) {
@@ -2435,7 +2435,7 @@
   // Store some user data.
   {
     base::RunLoop loop1;
-    registry()->StoreUserData(
+    registry().StoreUserData(
         registration1->id(), kKey1,
         {{"key1", "value1"}, {"prefixed_key1", "prefixed_value1"}},
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
@@ -2444,7 +2444,7 @@
         }));
 
     base::RunLoop loop2;
-    registry()->StoreUserData(
+    registry().StoreUserData(
         registration2->id(), kKey2,
         {{"key2", "value2"}, {"prefixed_key2", "prefixed_value2"}},
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
@@ -2463,7 +2463,7 @@
   // Tests that get methods for `registration1` work.
   {
     base::RunLoop loop1;
-    registry()->GetUserData(
+    registry().GetUserData(
         registration1->id(), {{"key1"}},
         base::BindLambdaForTesting([&](const std::vector<std::string>& values,
                                        blink::ServiceWorkerStatusCode status) {
@@ -2473,7 +2473,7 @@
         }));
 
     base::RunLoop loop2;
-    registry()->GetUserDataByKeyPrefix(
+    registry().GetUserDataByKeyPrefix(
         registration1->id(), "prefixed",
         base::BindLambdaForTesting([&](const std::vector<std::string>& values,
                                        blink::ServiceWorkerStatusCode status) {
@@ -2483,7 +2483,7 @@
         }));
 
     base::RunLoop loop3;
-    registry()->GetUserKeysAndDataByKeyPrefix(
+    registry().GetUserKeysAndDataByKeyPrefix(
         registration1->id(), "prefixed",
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
@@ -2505,7 +2505,7 @@
   // Tests that get methods for all registrations work.
   {
     base::RunLoop loop1;
-    registry()->GetUserDataForAllRegistrations(
+    registry().GetUserDataForAllRegistrations(
         "key2",
         base::BindLambdaForTesting(
             [&](const std::vector<std::pair<int64_t, std::string>>& user_data,
@@ -2516,7 +2516,7 @@
             }));
 
     base::RunLoop loop2;
-    registry()->GetUserDataForAllRegistrationsByKeyPrefix(
+    registry().GetUserDataForAllRegistrationsByKeyPrefix(
         "prefixed",
         base::BindLambdaForTesting(
             [&](const std::vector<std::pair<int64_t, std::string>>& user_data,
@@ -2537,7 +2537,7 @@
   // Tests that clear methods work.
   {
     base::RunLoop loop1;
-    registry()->ClearUserData(
+    registry().ClearUserData(
         registration1->id(), {{"key1"}},
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2545,7 +2545,7 @@
         }));
 
     base::RunLoop loop2;
-    registry()->ClearUserDataByKeyPrefixes(
+    registry().ClearUserDataByKeyPrefixes(
         registration2->id(), {{"key2"}},
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2553,7 +2553,7 @@
         }));
 
     base::RunLoop loop3;
-    registry()->ClearUserDataForAllRegistrationsByKeyPrefix(
+    registry().ClearUserDataForAllRegistrationsByKeyPrefix(
         "prefixed",
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -2587,7 +2587,7 @@
 
   ASSERT_EQ(StoreRegistration(registration, registration->waiting_version()),
             blink::ServiceWorkerStatusCode::kOk);
-  EXPECT_FALSE(registry()->ShouldPurgeOnShutdownForTesting(kKey));
+  EXPECT_FALSE(registry().ShouldPurgeOnShutdownForTesting(kKey));
 
   // Update storage policy to mark the origin should be purged on shutdown.
   special_storage_policy()->AddSessionOnly(kOrigin.GetURL());
@@ -2599,7 +2599,7 @@
   // All Mojo calls must be done at this point.
   EXPECT_EQ(inflight_call_count(), 0U);
 
-  EXPECT_TRUE(registry()->ShouldPurgeOnShutdownForTesting(kKey));
+  EXPECT_TRUE(registry().ShouldPurgeOnShutdownForTesting(kKey));
 }
 
 // Regression test for https://crbug.com/1165784.
@@ -2609,7 +2609,7 @@
 TEST_F(ServiceWorkerRegistryTest, DestroyRegistryDuringInflightCall) {
   {
     base::RunLoop loop;
-    registry()->GetRegisteredStorageKeys(base::BindLambdaForTesting(
+    registry().GetRegisteredStorageKeys(base::BindLambdaForTesting(
         [&](const std::vector<blink::StorageKey>& storage_keys) {
           EXPECT_TRUE(storage_keys.empty());
           loop.Quit();
@@ -2620,7 +2620,7 @@
 
   {
     base::RunLoop loop;
-    registry()->GetStorageUsageForStorageKey(
+    registry().GetStorageUsageForStorageKey(
         blink::StorageKey::CreateFromStringForTesting("https://example.com/"),
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status, int64_t usage) {
@@ -2633,7 +2633,7 @@
 
   {
     base::RunLoop loop;
-    registry()->PerformStorageCleanup(loop.QuitClosure());
+    registry().PerformStorageCleanup(loop.QuitClosure());
     SimulateRestart();
     loop.Run();
   }
@@ -2642,7 +2642,7 @@
 TEST_F(ServiceWorkerRegistryTest,
        DestroyRegistryDuringInflightCall_StoreUserData) {
   base::RunLoop loop;
-  registry()->StoreUserData(
+  registry().StoreUserData(
       /*registration_id=*/1,
       blink::StorageKey::CreateFromStringForTesting("https://example.com/"),
       {{"key", "value"}},
@@ -2657,7 +2657,7 @@
 TEST_F(ServiceWorkerRegistryTest,
        DestroyRegistryDuringInflightCall_ClearUserData) {
   base::RunLoop loop;
-  registry()->ClearUserData(
+  registry().ClearUserData(
       /*registration_id=*/1, {{"key"}},
       base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
         EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kErrorFailed);
@@ -2670,7 +2670,7 @@
 TEST_F(ServiceWorkerRegistryTest,
        DestroyRegistryDuringInflightCall_ClearUserDataByKeyPrefixes) {
   base::RunLoop loop;
-  registry()->ClearUserDataByKeyPrefixes(
+  registry().ClearUserDataByKeyPrefixes(
       /*registration_id=*/1, {{"prefix"}},
       base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
         EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kErrorFailed);
@@ -2684,7 +2684,7 @@
     ServiceWorkerRegistryTest,
     DestroyRegistryDuringInflightCall_ClearUserDataForAllRegistrationsByKeyPrefix) {
   base::RunLoop loop;
-  registry()->ClearUserDataForAllRegistrationsByKeyPrefix(
+  registry().ClearUserDataForAllRegistrationsByKeyPrefix(
       "prefix",
       base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
         EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kErrorFailed);
@@ -2697,7 +2697,7 @@
 TEST_F(ServiceWorkerRegistryTest,
        DestroyRegistryDuringInflightCall_GetUserDataForAllRegistrations) {
   base::RunLoop loop;
-  registry()->GetUserDataForAllRegistrations(
+  registry().GetUserDataForAllRegistrations(
       "key",
       base::BindLambdaForTesting(
           [&](const std::vector<std::pair<int64_t, std::string>>& user_data,
@@ -2713,7 +2713,7 @@
     ServiceWorkerRegistryTest,
     DestroyRegistryDuringInflightCall_GetUserDataForAllRegistrationsByKeyPrefix) {
   base::RunLoop loop;
-  registry()->GetUserDataForAllRegistrationsByKeyPrefix(
+  registry().GetUserDataForAllRegistrationsByKeyPrefix(
       "prefix",
       base::BindLambdaForTesting(
           [&](const std::vector<std::pair<int64_t, std::string>>& user_data,
@@ -3013,8 +3013,8 @@
     // Add the resources ids to the uncommitted list.
     const blink::StorageKey key =
         blink::StorageKey::CreateFirstParty(url::Origin::Create(scope_));
-    registry()->StoreUncommittedResourceId(resource_id1_, key);
-    registry()->StoreUncommittedResourceId(resource_id2_, key);
+    registry().StoreUncommittedResourceId(resource_id1_, key);
+    registry().StoreUncommittedResourceId(resource_id2_, key);
     EnsureRemoteCallsAreExecuted();
 
     std::vector<int64_t> verify_ids = GetUncommittedResourceIds();
@@ -3180,8 +3180,8 @@
   // Promote the worker to active and add a controllee.
   registration_->SetActiveVersion(registration_->waiting_version());
   registration_->active_version()->SetStatus(ServiceWorkerVersion::ACTIVATED);
-  registry()->UpdateToActiveState(registration_->id(), registration_->key(),
-                                  base::DoNothing());
+  registry().UpdateToActiveState(registration_->id(), registration_->key(),
+                                 base::DoNothing());
   ScopedServiceWorkerClient service_worker_client =
       CreateServiceWorkerClient(context());
   registration_->active_version()->AddControllee(service_worker_client.get());
@@ -3212,8 +3212,8 @@
   // Promote the worker to active worker and add a controllee.
   registration_->SetActiveVersion(registration_->waiting_version());
   registration_->active_version()->SetStatus(ServiceWorkerVersion::ACTIVATED);
-  registry()->UpdateToActiveState(registration_->id(), registration_->key(),
-                                  base::DoNothing());
+  registry().UpdateToActiveState(registration_->id(), registration_->key(),
+                                 base::DoNothing());
   ScopedServiceWorkerClient service_worker_client =
       CreateServiceWorkerClient(context());
   registration_->active_version()->AddControllee(service_worker_client.get());
@@ -3261,8 +3261,8 @@
 TEST_F(ServiceWorkerRegistryResourceTest, UpdateRegistration_NoLiveVersion) {
   // Promote the worker to active worker and add a controllee.
   registration_->SetActiveVersion(registration_->waiting_version());
-  registry()->UpdateToActiveState(registration_->id(), registration_->key(),
-                                  base::DoNothing());
+  registry().UpdateToActiveState(registration_->id(), registration_->key(),
+                                 base::DoNothing());
 
   // Make an updated registration.
   scoped_refptr<ServiceWorkerVersion> live_version =
@@ -3300,8 +3300,8 @@
   registration_->SetActiveVersion(registration_->waiting_version());
   registration_->active_version()->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetWaitingVersion(nullptr);
-  registry()->UpdateToActiveState(registration_->id(), registration_->key(),
-                                  base::DoNothing());
+  registry().UpdateToActiveState(registration_->id(), registration_->key(),
+                                 base::DoNothing());
   ScopedServiceWorkerClient service_worker_client =
       CreateServiceWorkerClient(context());
   registration_->active_version()->AddControllee(service_worker_client.get());
@@ -3318,8 +3318,8 @@
 
   // Also add an uncommitted resource.
   int64_t kStaleUncommittedResourceId = GetNewResourceIdSync(storage_control());
-  registry()->StoreUncommittedResourceId(kStaleUncommittedResourceId,
-                                         registration_->key());
+  registry().StoreUncommittedResourceId(kStaleUncommittedResourceId,
+                                        registration_->key());
   EnsureRemoteCallsAreExecuted();
   verify_ids = GetUncommittedResourceIds();
   EXPECT_EQ(1u, verify_ids.size());
@@ -3336,7 +3336,7 @@
   storage_control()->SetPurgingCompleteCallbackForTest(loop.QuitClosure());
   int64_t kNewResourceId = GetNewResourceIdSync(storage_control());
   WriteBasicResponse(storage_control(), kNewResourceId);
-  registry()->StoreUncommittedResourceId(kNewResourceId, registration_->key());
+  registry().StoreUncommittedResourceId(kNewResourceId, registration_->key());
   loop.Run();
 
   // The stale resources should be purged, but the new resource should persist.
@@ -3403,7 +3403,7 @@
 TEST_F(ServiceWorkerRegistryResourceTest, RetryInflightCalls_Resources) {
   const int64_t kResourceId = GetNewResourceIdSync(storage_control());
 
-  registry()->StoreUncommittedResourceId(kResourceId, registration_->key());
+  registry().StoreUncommittedResourceId(kResourceId, registration_->key());
   EXPECT_EQ(inflight_call_count(), 1U);
 
   helper()->SimulateStorageRestartForTesting();
@@ -3413,7 +3413,7 @@
   EXPECT_THAT(GetUncommittedResourceIds(),
               testing::UnorderedElementsAreArray({kResourceId}));
 
-  registry()->DoomUncommittedResource(kResourceId);
+  registry().DoomUncommittedResource(kResourceId);
   EXPECT_EQ(inflight_call_count(), 1U);
 
   helper()->SimulateStorageRestartForTesting();
diff --git a/content/browser/service_worker/service_worker_script_cache_map.cc b/content/browser/service_worker/service_worker_script_cache_map.cc
index 37902dd..3267d2e 100644
--- a/content/browser/service_worker/service_worker_script_cache_map.cc
+++ b/content/browser/service_worker/service_worker_script_cache_map.cc
@@ -53,7 +53,7 @@
   }
   resource_map_[url] = storage::mojom::ServiceWorkerResourceRecord::New(
       resource_id, url, -1, /*sha256_checksum=*/"");
-  context_->registry()->StoreUncommittedResourceId(resource_id, owner_->key());
+  context_->registry().StoreUncommittedResourceId(resource_id, owner_->key());
 }
 
 void ServiceWorkerScriptCacheMap::NotifyFinishedCaching(
@@ -72,7 +72,7 @@
     return;  // Our storage has been wiped via DeleteAndStartOver.
 
   if (net_error != net::OK) {
-    context_->registry()->DoomUncommittedResource(LookupResourceId(url));
+    context_->registry().DoomUncommittedResource(LookupResourceId(url));
     resource_map_.erase(url);
     if (owner_->script_url() == url) {
       main_script_net_error_ = net_error;
diff --git a/content/browser/service_worker/service_worker_script_loader_factory.cc b/content/browser/service_worker/service_worker_script_loader_factory.cc
index 5d2f6fa..536e96a 100644
--- a/content/browser/service_worker/service_worker_script_loader_factory.cc
+++ b/content/browser/service_worker/service_worker_script_loader_factory.cc
@@ -82,7 +82,7 @@
       version->script_cache_map()->LookupResourceId(resource_request.url);
   if (resource_id != blink::mojom::kInvalidServiceWorkerResourceId) {
     mojo::Remote<storage::mojom::ServiceWorkerResourceReader> resource_reader;
-    context_->registry()->GetRemoteStorageControl()->CreateResourceReader(
+    context_->registry().GetRemoteStorageControl()->CreateResourceReader(
         resource_id, resource_reader.BindNewPipeAndPassReceiver());
     mojo::MakeSelfOwnedReceiver(
         std::make_unique<ServiceWorkerInstalledScriptLoader>(
@@ -199,10 +199,10 @@
     return;
   }
   mojo::Remote<storage::mojom::ServiceWorkerResourceReader> reader;
-  context_->registry()->GetRemoteStorageControl()->CreateResourceReader(
+  context_->registry().GetRemoteStorageControl()->CreateResourceReader(
       resource_id, reader.BindNewPipeAndPassReceiver());
   mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer;
-  context_->registry()->GetRemoteStorageControl()->CreateResourceWriter(
+  context_->registry().GetRemoteStorageControl()->CreateResourceWriter(
       new_resource_id, writer.BindNewPipeAndPassReceiver());
 
   cache_writer_ = ServiceWorkerCacheWriter::CreateForCopy(
@@ -260,7 +260,7 @@
 
   // Use ServiceWorkerInstalledScriptLoader to load the new copy.
   mojo::Remote<storage::mojom::ServiceWorkerResourceReader> resource_reader;
-  context_->registry()->GetRemoteStorageControl()->CreateResourceReader(
+  context_->registry().GetRemoteStorageControl()->CreateResourceReader(
       new_resource_id, resource_reader.BindNewPipeAndPassReceiver());
   mojo::MakeSelfOwnedReceiver(
       std::make_unique<ServiceWorkerInstalledScriptLoader>(
diff --git a/content/browser/service_worker/service_worker_test_utils.cc b/content/browser/service_worker/service_worker_test_utils.cc
index 25d4d345..e62011c4 100644
--- a/content/browser/service_worker/service_worker_test_utils.cc
+++ b/content/browser/service_worker/service_worker_test_utils.cc
@@ -459,7 +459,7 @@
 }
 
 scoped_refptr<ServiceWorkerRegistration> CreateNewServiceWorkerRegistration(
-    ServiceWorkerRegistry* registry,
+    ServiceWorkerRegistry& registry,
     const blink::mojom::ServiceWorkerRegistrationOptions& options,
     const blink::StorageKey& key) {
   scoped_refptr<ServiceWorkerRegistration> registration;
@@ -473,7 +473,7 @@
   // TODO(bashi): Figure out a way to avoid using nested loop as it's
   // problematic.
   base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-  registry->CreateNewRegistration(
+  registry.CreateNewRegistration(
       options, key, blink::mojom::AncestorFrameType::kNormalFrame,
       base::BindLambdaForTesting(
           [&](scoped_refptr<ServiceWorkerRegistration> new_registration) {
@@ -486,7 +486,7 @@
 }
 
 scoped_refptr<ServiceWorkerVersion> CreateNewServiceWorkerVersion(
-    ServiceWorkerRegistry* registry,
+    ServiceWorkerRegistry& registry,
     scoped_refptr<ServiceWorkerRegistration> registration,
     const GURL& script_url,
     blink::mojom::ScriptType script_type) {
@@ -494,7 +494,7 @@
   // See comments in CreateNewServiceWorkerRegistration() why nestable tasks
   // allowed.
   base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-  registry->CreateNewVersion(
+  registry.CreateNewVersion(
       std::move(registration), script_url, script_type,
       base::BindLambdaForTesting(
           [&](scoped_refptr<ServiceWorkerVersion> new_version) {
@@ -789,21 +789,21 @@
   mojo::Remote<storage::mojom::ServiceWorkerResourceReader> compare_reader;
   worker_test_helper->context()
       ->registry()
-      ->GetRemoteStorageControl()
+      .GetRemoteStorageControl()
       ->CreateResourceReader(old_resource_id,
                              compare_reader.BindNewPipeAndPassReceiver());
 
   mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader;
   worker_test_helper->context()
       ->registry()
-      ->GetRemoteStorageControl()
+      .GetRemoteStorageControl()
       ->CreateResourceReader(old_resource_id,
                              copy_reader.BindNewPipeAndPassReceiver());
 
   mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer;
   worker_test_helper->context()
       ->registry()
-      ->GetRemoteStorageControl()
+      .GetRemoteStorageControl()
       ->CreateResourceWriter(new_resource_id,
                              writer.BindNewPipeAndPassReceiver());
 
diff --git a/content/browser/service_worker/service_worker_test_utils.h b/content/browser/service_worker/service_worker_test_utils.h
index 30d48ec..ae7b079 100644
--- a/content/browser/service_worker/service_worker_test_utils.h
+++ b/content/browser/service_worker/service_worker_test_utils.h
@@ -155,13 +155,13 @@
 
 // Calls CreateNewRegistration() synchronously.
 scoped_refptr<ServiceWorkerRegistration> CreateNewServiceWorkerRegistration(
-    ServiceWorkerRegistry* registry,
+    ServiceWorkerRegistry& registry,
     const blink::mojom::ServiceWorkerRegistrationOptions& options,
     const blink::StorageKey& key);
 
 // Calls CreateNewVersion() synchronously.
 scoped_refptr<ServiceWorkerVersion> CreateNewServiceWorkerVersion(
-    ServiceWorkerRegistry* registry,
+    ServiceWorkerRegistry& registry,
     scoped_refptr<ServiceWorkerRegistration> registration,
     const GURL& script_url,
     blink::mojom::ScriptType script_type);
diff --git a/content/browser/service_worker/service_worker_unregister_job.cc b/content/browser/service_worker/service_worker_unregister_job.cc
index 7dac757c..a903336 100644
--- a/content/browser/service_worker/service_worker_unregister_job.cc
+++ b/content/browser/service_worker/service_worker_unregister_job.cc
@@ -34,7 +34,7 @@
 }
 
 void ServiceWorkerUnregisterJob::Start() {
-  context_->registry()->FindRegistrationForScope(
+  context_->registry().FindRegistrationForScope(
       scope_, key_,
       base::BindOnce(&ServiceWorkerUnregisterJob::OnRegistrationFound,
                      weak_factory_.GetWeakPtr()));
diff --git a/content/browser/service_worker/service_worker_update_checker.cc b/content/browser/service_worker/service_worker_update_checker.cc
index 1c83ca6..8ad2efa9 100644
--- a/content/browser/service_worker/service_worker_update_checker.cc
+++ b/content/browser/service_worker/service_worker_update_checker.cc
@@ -227,19 +227,19 @@
   // cache map and it doesn't issue network request.
   const bool is_main_script = url == main_script_url_;
 
-  ServiceWorkerRegistry* registry = version_to_update_->context()->registry();
+  ServiceWorkerRegistry& registry = version_to_update_->context()->registry();
 
   // We need two identical readers for comparing and reading the resource for
   // |resource_id| from the storage.
   mojo::Remote<storage::mojom::ServiceWorkerResourceReader> compare_reader;
-  registry->GetRemoteStorageControl()->CreateResourceReader(
+  registry.GetRemoteStorageControl()->CreateResourceReader(
       resource_id, compare_reader.BindNewPipeAndPassReceiver());
   mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader;
-  registry->GetRemoteStorageControl()->CreateResourceReader(
+  registry.GetRemoteStorageControl()->CreateResourceReader(
       resource_id, copy_reader.BindNewPipeAndPassReceiver());
 
   mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer;
-  registry->GetRemoteStorageControl()->CreateResourceWriter(
+  registry.GetRemoteStorageControl()->CreateResourceWriter(
       new_resource_id, writer.BindNewPipeAndPassReceiver());
 
   running_checker_ = std::make_unique<ServiceWorkerSingleScriptUpdateChecker>(
diff --git a/content/browser/service_worker/service_worker_usb_delegate_observer_unittest.cc b/content/browser/service_worker/service_worker_usb_delegate_observer_unittest.cc
index 07078310..6634da25 100644
--- a/content/browser/service_worker/service_worker_usb_delegate_observer_unittest.cc
+++ b/content/browser/service_worker/service_worker_usb_delegate_observer_unittest.cc
@@ -227,8 +227,8 @@
     TestFuture<blink::ServiceWorkerStatusCode,
                scoped_refptr<ServiceWorkerRegistration>>
         future;
-    registry()->FindRegistrationForId(registration_id, key,
-                                      future.GetCallback());
+    registry().FindRegistrationForId(registration_id, key,
+                                     future.GetCallback());
     return future.Take();
   }
 
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 3755b288..706c95d4 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -591,7 +591,7 @@
   // Ensure the live registration during starting worker so that the worker can
   // get associated with it in
   // ServiceWorkerHost::CompleteStartWorkerPreparation.
-  context_->registry()->FindRegistrationForId(
+  context_->registry().FindRegistrationForId(
       registration_id_, key_,
       base::BindOnce(
           &ServiceWorkerVersion::DidEnsureLiveRegistrationForStartWorker,
@@ -713,7 +713,7 @@
   if (!context_) {
     return;
   }
-  context_->registry()->FindRegistrationForId(
+  context_->registry().FindRegistrationForId(
       registration_id_, key_,
       base::BindOnce(&ServiceWorkerVersion::FoundRegistrationForUpdate,
                      weak_factory_.GetWeakPtr()));
@@ -1489,7 +1489,7 @@
 
   if (status == blink::ServiceWorkerStatusCode::kOk) {
     if (fetch_handler_type_ && fetch_handler_type_ != new_fetch_handler_type) {
-      context_->registry()->UpdateFetchHandlerType(
+      context_->registry().UpdateFetchHandlerType(
           registration_id_, key_, new_fetch_handler_type,
           // Ignore errors; bumping the update fetch handler type is
           // just best-effort.
diff --git a/content/browser/service_worker/service_worker_version_browsertest.cc b/content/browser/service_worker/service_worker_version_browsertest.cc
index 7690c5db..f7bc704f 100644
--- a/content/browser/service_worker/service_worker_version_browsertest.cc
+++ b/content/browser/service_worker/service_worker_version_browsertest.cc
@@ -448,7 +448,7 @@
         wrapper()->context()->registry(), registration_.get(),
         embedded_test_server()->GetURL(worker_url), script_type);
     // Make the registration findable via storage functions.
-    wrapper()->context()->registry()->NotifyInstallingRegistration(
+    wrapper()->context()->registry().NotifyInstallingRegistration(
         registration_.get());
   }
 
@@ -509,7 +509,7 @@
     blink::ServiceWorkerStatusCode status;
     ServiceWorkerVersion* version =
         wrapper()->context()->GetLiveVersion(version_id);
-    wrapper()->context()->registry()->StoreRegistration(
+    wrapper()->context()->registry().StoreRegistration(
         registration_.get(), version,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode actual_status) {
@@ -519,7 +519,7 @@
             }));
     run_loop.Run();
 
-    wrapper()->context()->registry()->NotifyDoneInstallingRegistration(
+    wrapper()->context()->registry().NotifyDoneInstallingRegistration(
         registration_.get(), version_.get(), status);
   }
 
@@ -555,7 +555,7 @@
     blink::ServiceWorkerStatusCode status =
         blink::ServiceWorkerStatusCode::kErrorFailed;
     base::RunLoop run_loop;
-    wrapper()->context()->registry()->FindRegistrationForId(
+    wrapper()->context()->registry().FindRegistrationForId(
         id, key,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode actual_status,
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 51d82ce9..5bdae766 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -137,7 +137,7 @@
     // Make the registration findable via storage functions.
     std::optional<blink::ServiceWorkerStatusCode> status;
     base::RunLoop run_loop;
-    helper_->context()->registry()->StoreRegistration(
+    helper_->context()->registry().StoreRegistration(
         registration_.get(), version_.get(),
         ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure()));
     run_loop.Run();
@@ -442,7 +442,7 @@
   // Delete the registration.
   std::optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
-  helper_->context()->registry()->DeleteRegistration(
+  helper_->context()->registry().DeleteRegistration(
       registration_,
       ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure()));
   run_loop.Run();
@@ -1798,7 +1798,7 @@
   const std::string kMetadata("Test metadata");
 
   base::RunLoop loop;
-  helper_->context()->registry()->DisableStorageForTesting(loop.QuitClosure());
+  helper_->context()->registry().DisableStorageForTesting(loop.QuitClosure());
   loop.Run();
 
   net::TestCompletionCallback completion;
diff --git a/content/browser/shared_storage/shared_storage_event_params.cc b/content/browser/shared_storage/shared_storage_event_params.cc
index 64fd512..bc42bfa 100644
--- a/content/browser/shared_storage/shared_storage_event_params.cc
+++ b/content/browser/shared_storage/shared_storage_event_params.cc
@@ -15,6 +15,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
+#include "base/strings/to_string.h"
 #include "content/browser/private_aggregation/private_aggregation_host.h"
 #include "third_party/blink/public/mojom/shared_storage/shared_storage.mojom.h"
 
diff --git a/content/browser/tracing/background_tracing_manager_browsertest.cc b/content/browser/tracing/background_tracing_manager_browsertest.cc
index 85cb069..2d1d2eb 100644
--- a/content/browser/tracing/background_tracing_manager_browsertest.cc
+++ b/content/browser/tracing/background_tracing_manager_browsertest.cc
@@ -32,8 +32,9 @@
 #include "base/test/test_proto_loader.h"
 #include "base/test/trace_event_analyzer.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/trace_event/interned_args_helper.h"
 #include "base/trace_event/named_trigger.h"
-#include "base/trace_event/trace_event.h"
+#include "base/trace_event/typed_macros.h"
 #include "build/build_config.h"
 #include "content/browser/devtools/protocol/devtools_protocol_test_support.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
@@ -57,6 +58,7 @@
 #include "services/tracing/public/cpp/tracing_features.h"
 #include "third_party/perfetto/include/perfetto/ext/trace_processor/export_json.h"
 #include "third_party/perfetto/include/perfetto/trace_processor/trace_processor_storage.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "third_party/zlib/google/compression_utils.h"
 #include "third_party/zlib/zlib.h"
diff --git a/content/browser/tracing/background_tracing_manager_unittest.cc b/content/browser/tracing/background_tracing_manager_unittest.cc
index db92380c..b0cfb65 100644
--- a/content/browser/tracing/background_tracing_manager_unittest.cc
+++ b/content/browser/tracing/background_tracing_manager_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_proto_loader.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/token.h"
 #include "base/values.h"
 #include "build/build_config.h"
diff --git a/content/browser/tracing/file_tracing_provider_impl.cc b/content/browser/tracing/file_tracing_provider_impl.cc
index 01f762ec..bd243431 100644
--- a/content/browser/tracing/file_tracing_provider_impl.cc
+++ b/content/browser/tracing/file_tracing_provider_impl.cc
@@ -5,7 +5,7 @@
 #include "content/browser/tracing/file_tracing_provider_impl.h"
 
 #include "base/files/file_path.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 namespace content {
 
diff --git a/content/browser/url_info.cc b/content/browser/url_info.cc
index c6a0218..f29ee58 100644
--- a/content/browser/url_info.cc
+++ b/content/browser/url_info.cc
@@ -4,6 +4,8 @@
 
 #include "content/browser/url_info.h"
 
+#include <sstream>
+
 #include "content/browser/isolation_context.h"
 
 namespace content {
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
index 5706cff..8f0df10 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
@@ -69,7 +69,7 @@
         public boolean shouldBeInLowRankGroup() {
             boolean inViewport = visible && (frameDepth == 0 || intersectsViewport);
             return (isSpareRenderer && ChildProcessRanking.isSpareRendererOfLowestRanking())
-                    || (importance == ChildProcessImportance.NORMAL && !inViewport);
+                    || (importance <= ChildProcessImportance.PERCEPTIBLE && !inViewport);
         }
     }
 
@@ -93,9 +93,11 @@
             // Ranking order:
             // * (visible and main frame) or ChildProcessImportance.IMPORTANT
             // * (visible and subframe and intersect viewport) or ChildProcessImportance.MODERATE
-            // * ChildProcessImportance.PERCEPTIBLE
             // ---- cutoff for shouldBeInLowRankGroup ----
             // * visible subframe and not intersect viewport
+            //   * These processes are bound with NotPerceptibleBinding by BindingManager in
+            //   * practice.
+            // * ChildProcessImportance.PERCEPTIBLE
             // * invisible main and sub frames (not ranked by frame depth)
             // * spare renderer (if lowest-ranking parameter is set).
             // Within each group, ties are broken by intersect viewport and then frame depth where
@@ -131,6 +133,14 @@
                 return 1;
             }
 
+            if (o1.visible && o2.visible) {
+                return compareByIntersectsViewportAndDepth(o1, o2);
+            } else if (o1.visible && !o2.visible) {
+                return -1;
+            } else if (!o1.visible && o2.visible) {
+                return 1;
+            }
+
             boolean o1Perceptible = o1.importance == ChildProcessImportance.PERCEPTIBLE;
             boolean o2Perceptible = o2.importance == ChildProcessImportance.PERCEPTIBLE;
             if (o1Perceptible && o2Perceptible) {
@@ -141,14 +151,6 @@
                 return 1;
             }
 
-            if (o1.visible && o2.visible) {
-                return compareByIntersectsViewportAndDepth(o1, o2);
-            } else if (o1.visible && !o2.visible) {
-                return -1;
-            } else if (!o1.visible && o2.visible) {
-                return 1;
-            }
-
             if (isSpareRendererOfLowestRanking()) {
                 if (!o1.isSpareRenderer && o2.isSpareRenderer) {
                     return -1;
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ScreenCapture.java b/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ScreenCapture.java
index caaf87f..96933e9 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ScreenCapture.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ScreenCapture.java
@@ -4,6 +4,8 @@
 
 package org.chromium.content_public.browser.media.capture;
 
+import androidx.activity.result.ActivityResult;
+
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
 import org.jni_zero.NativeMethods;
@@ -11,6 +13,7 @@
 import org.chromium.build.annotations.NullMarked;
 
 import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicReference;
 
 /** See comments on `DesktopCapturerAndroid`. */
 @NullMarked
@@ -18,12 +21,29 @@
 public class ScreenCapture {
     private static final String TAG = "ScreenCapture";
 
+    // Starting a MediaProjection session involves plumbing the results from the content picker,
+    // which is done via ActivityResult. This class does not handle how that is achieved, but
+    // requires the ActivityResult to begin the session.
+    private static final AtomicReference<ActivityResult> sNextResult = new AtomicReference(null);
+
     private long mNativeDesktopCapturerAndroid;
 
     private ScreenCapture(long nativeDesktopCapturerAndroid) {
         mNativeDesktopCapturerAndroid = nativeDesktopCapturerAndroid;
     }
 
+    /**
+     * Called before attempting to start a ScreenCapture session.
+     *
+     * <p>The {@link ActivityResult} is consumed by a subsequent call to {@link #startCapture()}.
+     *
+     * @param nextResult The {@link ActivityResult} from the MediaProjection API.
+     */
+    public static void onPick(ActivityResult nextResult) {
+        var oldResult = sNextResult.getAndSet(nextResult);
+        assert oldResult == null;
+    }
+
     @CalledByNative
     static ScreenCapture create(long nativeDesktopCapturerAndroid) {
         return new ScreenCapture(nativeDesktopCapturerAndroid);
diff --git a/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java b/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java
index e251358..e9fbc5f 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java
@@ -174,8 +174,10 @@
         ChildProcessConnection c3 = createConnection();
         ChildProcessConnection c4 = createConnection();
         ChildProcessConnection c5 = createConnection();
+        ChildProcessConnection c6 = createConnection();
+        ChildProcessConnection c7 = createConnection();
 
-        ChildProcessRanking ranking = new ChildProcessRanking(5);
+        ChildProcessRanking ranking = new ChildProcessRanking(7);
         ranking.enableServiceGroupImportance();
 
         // Insert in lowest ranked to highest ranked order.
@@ -195,28 +197,45 @@
                 ChildProcessImportance.PERCEPTIBLE);
         ranking.addConnection(
                 c3,
+                /* visible= */ true,
+                /* frameDepth= */ 1,
+                /* intersectsViewport= */ false,
+                /* isSpareRenderer= */ false,
+                ChildProcessImportance.NORMAL);
+        ranking.addConnection(
+                c4,
                 /* visible= */ false,
                 /* frameDepth= */ 0,
                 /* intersectsViewport= */ false,
                 /* isSpareRenderer= */ false,
                 ChildProcessImportance.MODERATE);
         ranking.addConnection(
-                c4,
+                c5,
                 /* visible= */ false,
                 /* frameDepth= */ 1,
                 /* intersectsViewport= */ false,
                 /* isSpareRenderer= */ false,
                 ChildProcessImportance.IMPORTANT);
         ranking.addConnection(
-                c5,
+                c6,
                 /* visible= */ false,
                 /* frameDepth= */ 0,
                 /* intersectsViewport= */ false,
                 /* isSpareRenderer= */ false,
                 ChildProcessImportance.IMPORTANT);
+        // Visible main frame should be ChildProcessImportance.MODERATE or higher. But there can be
+        // a race of inconsistency.
+        ranking.addConnection(
+                c7,
+                /* visible= */ true,
+                /* frameDepth= */ 0,
+                /* intersectsViewport= */ false,
+                /* isSpareRenderer= */ false,
+                ChildProcessImportance.PERCEPTIBLE);
 
-        assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c5, c4, c3, c2, c1});
-        assertNotInGroup(new ChildProcessConnection[] {c5, c4, c3, c2});
+        assertRankingAndRemoveAll(
+                ranking, new ChildProcessConnection[] {c7, c6, c5, c4, c3, c2, c1});
+        assertNotInGroup(new ChildProcessConnection[] {c7, c6, c5, c4});
         assertInGroupOrderedByImportance(new ChildProcessConnection[] {c1});
     }
 
diff --git a/content/public/browser/android/child_process_importance.h b/content/public/browser/android/child_process_importance.h
index 389f5d1..e5049669 100644
--- a/content/public/browser/android/child_process_importance.h
+++ b/content/public/browser/android/child_process_importance.h
@@ -21,6 +21,10 @@
 // Android version, PERCEPTIBLE importance falls back to NORMAL importance and
 // the corresponding waived service binding.
 //
+// Note that the numerical order in ChildProcessImportance should be consistent
+// because ChildProcessImportance is compared numerically in
+// ChildProcessRanking.java.
+//
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.content_public.browser
 enum class ChildProcessImportance {
   // NORMAL is the default value.
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 5bddb6e..15fe16a 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1000,6 +1000,13 @@
              "ServiceWorkerPaymentApps",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// If enabled, UI thread tasks can check ServiceWorker registration information
+// from the thread pool without waiting for running the receiving task. Please
+// see crbug.com/421530699 for more details.
+BASE_FEATURE(kServiceWorkerBackgroundUpdateForRegisteredStorageKeys,
+             "ServiceWorkerBackgroundUpdateForRegisteredStorageKeys",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // http://tc39.github.io/ecmascript_sharedmem/shmem.html
 // This feature is also enabled independently of this flag for cross-origin
 // isolated renderers.
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 8c17e37..bf7a6c36 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -224,6 +224,8 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSecurePaymentConfirmation);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSecurePaymentConfirmationDebug);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kServiceWorkerPaymentApps);
+CONTENT_EXPORT BASE_DECLARE_FEATURE(
+    kServiceWorkerBackgroundUpdateForRegisteredStorageKeys);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSharedArrayBuffer);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSiteInstanceGroupsForDataUrls);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kDefaultSiteInstanceGroups);
diff --git a/content/public/test/embedded_worker_instance_test_harness.cc b/content/public/test/embedded_worker_instance_test_harness.cc
index ed280d7b..2ca298c6 100644
--- a/content/public/test/embedded_worker_instance_test_harness.cc
+++ b/content/public/test/embedded_worker_instance_test_harness.cc
@@ -73,7 +73,7 @@
 
   // Make the registration findable via storage functions.
   base::test::TestFuture<blink::ServiceWorkerStatusCode> status;
-  helper_->context()->registry()->StoreRegistration(
+  helper_->context()->registry().StoreRegistration(
       pair.first.get(), pair.second.get(), status.GetCallback());
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, status.Get());
 
diff --git a/content/public/test/service_worker_test_helpers.cc b/content/public/test/service_worker_test_helpers.cc
index 5cdc80f..a046fef 100644
--- a/content/public/test/service_worker_test_helpers.cc
+++ b/content/public/test/service_worker_test_helpers.cc
@@ -229,7 +229,7 @@
       static_cast<ServiceWorkerContextWrapper*>(context));
   version_created_watcher_ =
       std::make_unique<ServiceWorkerVersionCreatedWatcher>(
-          context_wrapper->GetContextCoreForTest(), this);
+          context_wrapper->context(), this);
 }
 
 void ServiceWorkerTestHelper::RegisterStateObserver(
diff --git a/content/public/test/test_launcher.cc b/content/public/test/test_launcher.cc
index ebdc156f..9824760f 100644
--- a/content/public/test/test_launcher.cc
+++ b/content/public/test/test_launcher.cc
@@ -22,6 +22,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/functional/callback_helpers.h"
+#include "base/hash/hash.h"
 #include "base/i18n/icu_util.h"
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
diff --git a/content/public/test/test_navigation_throttle_inserter.cc b/content/public/test/test_navigation_throttle_inserter.cc
index 0c544aa9..9d5332ce 100644
--- a/content/public/test/test_navigation_throttle_inserter.cc
+++ b/content/public/test/test_navigation_throttle_inserter.cc
@@ -22,7 +22,7 @@
     NavigationHandle* navigation_handle) {
   if (callback_) {
     callback_.Run(*NavigationRequest::From(navigation_handle)
-                           ->GetNavigationThrottleRunnerForTesting());
+                           ->GetNavigationThrottleRegistryForTesting());
   }
 }
 
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index d166cb2..e3b3a52 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/feature_list.h"
 #include "base/format_macros.h"
 #include "base/functional/bind.h"
+#include "base/hash/hash.h"
 #include "base/json/json_writer.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index 5a29dc6f..76da81af 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -1135,7 +1135,7 @@
 
 NavigationThrottleRegistry&
 NavigationSimulatorImpl::GetNavigationThrottleRegistry() {
-  return *GetNavigationHandle()->GetNavigationThrottleRunnerForTesting();
+  return *GetNavigationHandle()->GetNavigationThrottleRegistryForTesting();
 }
 
 content::GlobalRequestID NavigationSimulatorImpl::GetGlobalRequestID() {
diff --git a/device/vr/android/cardboard/cardboard_render_loop.cc b/device/vr/android/cardboard/cardboard_render_loop.cc
index b971646..87163df 100644
--- a/device/vr/android/cardboard/cardboard_render_loop.cc
+++ b/device/vr/android/cardboard/cardboard_render_loop.cc
@@ -5,9 +5,11 @@
 #include "device/vr/android/cardboard/cardboard_render_loop.h"
 
 #include <time.h>
+
 #include <memory>
 
 #include "base/task/bind_post_task.h"
+#include "base/trace_event/trace_event.h"
 #include "device/vr/android/cardboard/cardboard_image_transport.h"
 #include "device/vr/android/cardboard/cardboard_sdk.h"
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
diff --git a/docs/navigation.md b/docs/navigation.md
index 3e4a425..0cdd19c7 100644
--- a/docs/navigation.md
+++ b/docs/navigation.md
@@ -157,7 +157,7 @@
 simulating a redirect), as discussed in
 [Navigation Concepts](navigation_concepts.md#rules-for-canceling-navigations).
 They are typically registered in
-`NavigationThrottleRunner::RegisterNavigationThrottles` or
+`NavigationThrottleRegistryImpl::RegisterNavigationThrottles` or
 `ContentBrowserClient::CreateThrottlesForNavigation`.
 
 The most common NavigationThrottles events are `WillStartRequest`,
@@ -167,7 +167,7 @@
 require a URLLoader (see NavigationRequest::NeedsUrlLoader).
 A NavigationThrottle that wishes to intercept a non-URLLoader navigation
 (same-document navigations, about:blank, etc.) should register itself in
-`NavigationThrottleRunner::RegisterNavigationThrottlesForCommitWithoutUrlLoader`,
+`NavigationThrottleRegistryImpl::RegisterNavigationThrottlesForCommitWithoutUrlLoader`,
 and will get a single `WillCommitWithoutUrlLoader` event instead of the full
 set of events centered on network requests. Page-activation navigations, such
 as activating a prerendered page or restoring a page from the back-forward
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 8b6a4d8e..321617a 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -2231,10 +2231,6 @@
   return ExtensionSystem::Get(browser_context_)->app_sorting();
 }
 
-bool ExtensionPrefs::NeedsStorageGarbageCollection() const {
-  return prefs_->GetBoolean(pref_names::kStorageGarbageCollect);
-}
-
 // static
 void ExtensionPrefs::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
@@ -2250,7 +2246,6 @@
   registry->RegisterDictionaryPref(pref_names::kOAuthRedirectUrls);
   registry->RegisterListPref(pref_names::kAllowedTypes);
   registry->RegisterIntegerPref(pref_names::kManifestV2Availability, 0);
-  registry->RegisterBooleanPref(pref_names::kStorageGarbageCollect, false);
   registry->RegisterListPref(pref_names::kAllowedInstallSites);
   registry->RegisterStringPref(pref_names::kLastChromeVersion, std::string());
   registry->RegisterDictionaryPref(kInstallSignature);
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h
index 6aff1b6..7c95120 100644
--- a/extensions/browser/extension_prefs.h
+++ b/extensions/browser/extension_prefs.h
@@ -710,11 +710,6 @@
   // The underlying AppSorting.
   AppSorting* app_sorting() const;
 
-  // Schedules garbage collection of an extension's on-disk data on the next
-  // start of this ExtensionService. Applies only to extensions with isolated
-  // storage.
-  bool NeedsStorageGarbageCollection() const;
-
   // Used by AppWindowGeometryCache to persist its cache. These methods
   // should not be called directly.
   const base::Value::Dict* GetGeometryCache(
diff --git a/extensions/browser/pref_names.h b/extensions/browser/pref_names.h
index 785ce7a..2aec75677 100644
--- a/extensions/browser/pref_names.h
+++ b/extensions/browser/pref_names.h
@@ -116,12 +116,6 @@
 // object stored in the Preferences file. The extensions are stored by ID.
 inline constexpr char kPinnedExtensions[] = "extensions.pinned_extensions";
 
-// Indicates on-disk data might have skeletal data that needs to be cleaned
-// on the next start of the browser.
-// TODO(crbug.com/40922689): Delete ExtensionsPref::kStorageGarbageCollect.
-inline constexpr char kStorageGarbageCollect[] =
-    "extensions.storage.garbagecollect";
-
 // Pref for policy to enable/disable loading extension from command line
 inline constexpr char kExtensionInstallTypeBlocklist[] =
     "extensions.extension_install_type_blocklist";
diff --git a/gin/array_buffer.cc b/gin/array_buffer.cc
index 1f86b7b..4a8807c 100644
--- a/gin/array_buffer.cc
+++ b/gin/array_buffer.cc
@@ -86,11 +86,6 @@
   opts.backup_ref_ptr = partition_alloc::PartitionOptions::kDisabled;
   opts.use_configurable_pool = partition_alloc::PartitionOptions::kAllowed;
 
-  // TODO(crbug.com/333443437): Remove this user-configurable toggle and
-  // default all buckets to "small" single-slot spans.
-  opts.use_small_single_slot_spans =
-      partition_alloc::PartitionOptions::kEnabled;
-
   static base::NoDestructor<partition_alloc::PartitionAllocator>
       partition_allocator(opts);
 
diff --git a/gin/v8_platform_thread_isolated_allocator.cc b/gin/v8_platform_thread_isolated_allocator.cc
index 11e2f65b..26331d0 100644
--- a/gin/v8_platform_thread_isolated_allocator.cc
+++ b/gin/v8_platform_thread_isolated_allocator.cc
@@ -30,11 +30,6 @@
   partition_alloc::PartitionOptions opts;
   opts.thread_isolation = partition_alloc::ThreadIsolationOption(pkey_);
 
-  // TODO(crbug.com/333443437): Remove this user-configurable toggle and
-  // default all buckets to "small" single-slot spans.
-  opts.use_small_single_slot_spans =
-      partition_alloc::PartitionOptions::kEnabled;
-
   allocator_.init(opts);
 }
 
diff --git a/infra/config/generated/builders/ci/android-15-tablet-x64-rel/properties.json b/infra/config/generated/builders/ci/android-15-tablet-x64-rel/properties.json
index 1b422b4..1ff3a9b4 100644
--- a/infra/config/generated/builders/ci/android-15-tablet-x64-rel/properties.json
+++ b/infra/config/generated/builders/ci/android-15-tablet-x64-rel/properties.json
@@ -77,5 +77,11 @@
     ]
   },
   "builder_group": "chromium.android",
-  "recipe": "chromium"
+  "gardener_rotations": [
+    "android"
+  ],
+  "recipe": "chromium",
+  "sheriff_rotations": [
+    "android"
+  ]
 }
\ No newline at end of file
diff --git a/infra/config/generated/cq-usage/mega_cq_bots.txt b/infra/config/generated/cq-usage/mega_cq_bots.txt
index af1e5a5..1657042 100644
--- a/infra/config/generated/cq-usage/mega_cq_bots.txt
+++ b/infra/config/generated/cq-usage/mega_cq_bots.txt
@@ -9,6 +9,7 @@
 chromium/try/android-14-arm64-rel
 chromium/try/android-14-tablet-landscape-arm64-rel
 chromium/try/android-15-tablet-landscape-x64-rel
+chromium/try/android-15-tablet-x64-rel
 chromium/try/android-15-x64-rel
 chromium/try/android-arm-compile-dbg
 chromium/try/android-arm64-rel
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index a510fc2..adf063c7 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -38349,8 +38349,14 @@
         '    }'
         '  },'
         '  "builder_group": "chromium.android",'
+        '  "gardener_rotations": ['
+        '    "android"'
+        '  ],'
         '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
+        '  "recipe": "chromium",'
+        '  "sheriff_rotations": ['
+        '    "android"'
+        '  ]'
         '}'
       execution_timeout_secs: 14400
       build_numbers: YES
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 14ee11e7..e75cfa0 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -3514,6 +3514,11 @@
     short_name: "13"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/android-15-tablet-x64-rel"
+    category: "chromium.android|builder_tester|x64"
+    short_name: "15T"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/android-15-tablet-landscape-x64-rel"
     category: "chromium.android|builder_tester|x64"
     short_name: "15T-L"
@@ -4022,6 +4027,11 @@
     short_name: "13"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/android-15-tablet-x64-rel"
+    category: "chromium.android|builder_tester|x64"
+    short_name: "15T"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/android-15-tablet-landscape-x64-rel"
     category: "chromium.android|builder_tester|x64"
     short_name: "15T-L"
diff --git a/infra/config/generated/luci/luci-notify.cfg b/infra/config/generated/luci/luci-notify.cfg
index 39d5fca..622cc77 100644
--- a/infra/config/generated/luci/luci-notify.cfg
+++ b/infra/config/generated/luci/luci-notify.cfg
@@ -2039,6 +2039,25 @@
   }
   builders {
     bucket: "ci"
+    name: "android-15-tablet-x64-rel"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|generate_build_files|runhooks|update|\\w*nocompile_test)\\b"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|generate_build_files|runhooks|update|\\w*nocompile_test)\\b"
+    email {
+      rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff"
+    }
+    template: "tree_closure_email_template"
+  }
+  builders {
+    bucket: "ci"
     name: "android-15-x64-rel"
     repository: "https://chromium.googlesource.com/chromium/src"
   }
diff --git a/infra/config/generated/sheriff-rotations/android.txt b/infra/config/generated/sheriff-rotations/android.txt
index e63a4a2..d5f68cd 100644
--- a/infra/config/generated/sheriff-rotations/android.txt
+++ b/infra/config/generated/sheriff-rotations/android.txt
@@ -19,6 +19,7 @@
 ci/android-14-arm64-rel
 ci/android-14-tablet-landscape-arm64-rel
 ci/android-15-tablet-landscape-x64-rel
+ci/android-15-tablet-x64-rel
 ci/android-15-x64-rel
 ci/android-androidx-packager
 ci/android-bfcache-rel
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star
index 75f1a51c1..54ab2bc 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -4407,9 +4407,7 @@
     targets_settings = targets.settings(
         os_type = targets.os_type.ANDROID,
     ),
-    # TODO(crbug.com/376748979 ): Enable gardening once tests are stable
-    gardener_rotations = args.ignore_default(None),
-    # tree_closing = True,
+    tree_closing = True,
     console_view_entry = consoles.console_view_entry(
         category = "builder_tester|x64",
         short_name = "15T",
diff --git a/ios/chrome/browser/credential_provider/model/ios_credential_provider_infobar_delegate_unittest.mm b/ios/chrome/browser/credential_provider/model/ios_credential_provider_infobar_delegate_unittest.mm
index e903df42..04f8f0e 100644
--- a/ios/chrome/browser/credential_provider/model/ios_credential_provider_infobar_delegate_unittest.mm
+++ b/ios/chrome/browser/credential_provider/model/ios_credential_provider_infobar_delegate_unittest.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/credential_provider/model/ios_credential_provider_infobar_delegate.h"
 
 #import "base/strings/sys_string_conversions.h"
+#import "base/strings/utf_string_conversions.h"
 #import "base/test/task_environment.h"
 #import "components/sync/protocol/webauthn_credential_specifics.pb.h"
 #import "ios/chrome/grit/ios_strings.h"
diff --git a/ios/chrome/browser/price_notifications/ui_bundled/price_notifications_price_tracking_mediator_unittest.mm b/ios/chrome/browser/price_notifications/ui_bundled/price_notifications_price_tracking_mediator_unittest.mm
index 018109c..96204fb7 100644
--- a/ios/chrome/browser/price_notifications/ui_bundled/price_notifications_price_tracking_mediator_unittest.mm
+++ b/ios/chrome/browser/price_notifications/ui_bundled/price_notifications_price_tracking_mediator_unittest.mm
@@ -12,6 +12,7 @@
 #import "base/memory/raw_ptr.h"
 #import "base/strings/string_number_conversions.h"
 #import "base/strings/sys_string_conversions.h"
+#import "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #import "components/bookmarks/test/bookmark_test_helpers.h"
 #import "components/commerce/core/mock_shopping_service.h"
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_test.mm b/ios/chrome/browser/reader_mode/model/reader_mode_test.mm
index a2f404ce..5e761bf 100644
--- a/ios/chrome/browser/reader_mode/model/reader_mode_test.mm
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_test.mm
@@ -6,6 +6,7 @@
 
 #import <memory>
 
+#import "base/strings/utf_string_conversions.h"
 #import "components/dom_distiller/core/extraction_utils.h"
 #import "ios/chrome/browser/reader_mode/model/features.h"
 #import "ios/chrome/browser/reader_mode/model/reader_mode_java_script_feature.h"
diff --git a/ios/chrome/browser/settings/ui_bundled/search_engine_table_view_controller_non_eea_unittest.mm b/ios/chrome/browser/settings/ui_bundled/search_engine_table_view_controller_non_eea_unittest.mm
index c1894c4..2cfc44f 100644
--- a/ios/chrome/browser/settings/ui_bundled/search_engine_table_view_controller_non_eea_unittest.mm
+++ b/ios/chrome/browser/settings/ui_bundled/search_engine_table_view_controller_non_eea_unittest.mm
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #import "base/command_line.h"
+#import "base/strings/utf_string_conversions.h"
 #import "base/test/scoped_feature_list.h"
 #import "components/regional_capabilities/regional_capabilities_switches.h"
 #import "components/search_engines/template_url_data_util.h"
diff --git a/ios/chrome/browser/settings/ui_bundled/search_engine_table_view_controller_unittest.mm b/ios/chrome/browser/settings/ui_bundled/search_engine_table_view_controller_unittest.mm
index 4bf24c3..bca31b3 100644
--- a/ios/chrome/browser/settings/ui_bundled/search_engine_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/settings/ui_bundled/search_engine_table_view_controller_unittest.mm
@@ -6,6 +6,7 @@
 
 #import "base/apple/foundation_util.h"
 #import "base/strings/sys_string_conversions.h"
+#import "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #import "ios/chrome/browser/favicon/model/favicon_service_factory.h"
 #import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h"
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index 74d6f92..1a0451f9 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -14,6 +14,7 @@
 #import "base/functional/callback_helpers.h"
 #import "base/logging.h"
 #import "base/strings/sys_string_conversions.h"
+#import "base/strings/utf_string_conversions.h"
 #import "base/task/sequenced_task_runner.h"
 #import "base/test/gmock_callback_support.h"
 #import "base/test/ios/wait_util.h"
diff --git a/ios/web/web_state/web_state_unittest.mm b/ios/web/web_state/web_state_unittest.mm
index 9660090..43bd83b 100644
--- a/ios/web/web_state/web_state_unittest.mm
+++ b/ios/web/web_state/web_state_unittest.mm
@@ -10,6 +10,7 @@
 #import "base/path_service.h"
 #import "base/run_loop.h"
 #import "base/strings/stringprintf.h"
+#import "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #import "base/test/metrics/histogram_tester.h"
 #import "base/values.h"
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc
index e60b3b9..75056be 100644
--- a/ipc/ipc_mojo_bootstrap.cc
+++ b/ipc/ipc_mojo_bootstrap.cc
@@ -21,6 +21,7 @@
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
+#include "base/hash/hash.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/no_destructor.h"
diff --git a/media/audio/mac/catap_audio_input_stream.mm b/media/audio/mac/catap_audio_input_stream.mm
index cb1bb16..a54026f8 100644
--- a/media/audio/mac/catap_audio_input_stream.mm
+++ b/media/audio/mac/catap_audio_input_stream.mm
@@ -51,19 +51,11 @@
              "MacCatapProbeTapOnCreation",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// When `kMacCatapCaptureAllDevices` is disabled:
-//
-// CatapAudioInputStream captures audio from the default output device. However,
-// if the device ID is explicitly set to `kLoopbackAllDevicesId`, it will
-// capture all system audio regardless of the specific output device used for
-// playback.
-//
-// When `kMacCatapCaptureAllDevices` is enabled:
-//
-// CatapAudioInputStream captures all system audio, irrespective of the specific
-// output device it's played on or the device ID set.
-BASE_FEATURE(kMacCatapCaptureAllDevices,
-             "MacCatapCaptureAllDevices",
+// If this feature is enabled, we will only capture the default output device.
+// If the feature is disabled, all system audio is captured regardless of which
+// output device the audio is played on.
+BASE_FEATURE(kMacCatapCaptureDefaultDevice,
+             "MacCatapCaptureDefaultDevice",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
 API_AVAILABLE(macos(14.2))
@@ -226,13 +218,7 @@
     }
   }
 
-  if (device_id_ == AudioDeviceDescription::kLoopbackAllDevicesId ||
-      base::FeatureList::IsEnabled(kMacCatapCaptureAllDevices)) {
-    // Mix all processes to a stereo stream except the given processes.
-    tap_description_ =
-        [[CATapDescription alloc] initStereoGlobalTapButExcludeProcesses:
-                                      process_audio_device_ids_to_exclude];
-  } else {
+  if (base::FeatureList::IsEnabled(kMacCatapCaptureDefaultDevice)) {
     // Mix all process audio streams destined for the selected device stream
     // except the given processes.
     tap_description_ = [[CATapDescription alloc]
@@ -240,6 +226,11 @@
                   andDeviceUID:[NSString stringWithUTF8String:
                                              default_output_device_id_.c_str()]
                     withStream:0];
+  } else {
+    // Mix all processes to a stereo stream except the given processes.
+    tap_description_ =
+        [[CATapDescription alloc] initStereoGlobalTapButExcludeProcesses:
+                                      process_audio_device_ids_to_exclude];
   }
 
   if (params_.channels() == 1) {
diff --git a/media/audio/mac/catap_audio_input_stream_unittest.mm b/media/audio/mac/catap_audio_input_stream_unittest.mm
index 088da49..b2d3a3a 100644
--- a/media/audio/mac/catap_audio_input_stream_unittest.mm
+++ b/media/audio/mac/catap_audio_input_stream_unittest.mm
@@ -3,16 +3,12 @@
 // found in the LICENSE file.
 #include "media/audio/mac/catap_audio_input_stream.h"
 
-#import <Foundation/Foundation.h>
-
 #include <memory>
 #include <set>
-#include <string>
 #include <utility>
 
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
-#include "base/strings/sys_string_conversions.h"
 #include "media/audio/audio_device_description.h"
 #include "media/audio/audio_manager.h"
 #include "media/audio/mac/audio_loopback_input_mac.h"
@@ -152,19 +148,12 @@
 
       // Set up expectations for a successful open.
       EXPECT_CALL(mock_catap_api(), AudioHardwareCreateProcessTap)
-          .WillOnce([](CATapDescription* in_description,
-                       AudioObjectID* out_tap) {
-            // Default device selected.
-            EXPECT_EQ(std::string(
-                          base::SysNSStringToUTF8([in_description deviceUID])),
-                      media::AudioDeviceDescription::kDefaultDeviceId);
-            // Expect the first stream to be selected.
-            EXPECT_EQ([[in_description stream] intValue], 0);
-            // Not muted during capture.
-            EXPECT_EQ([in_description isMuted], CATapUnmuted);
-            *out_tap = kTap;
-            return noErr;
-          });
+          .WillOnce(
+              [](CATapDescription* in_description, AudioObjectID* out_tap) {
+                EXPECT_EQ([in_description isMuted], CATapUnmuted);
+                *out_tap = kTap;
+                return noErr;
+              });
       EXPECT_CALL(mock_catap_api(), AudioHardwareCreateAggregateDevice)
           .WillOnce([](CFDictionaryRef in_device_properties,
                        AudioDeviceID* out_device) {
@@ -505,54 +494,4 @@
   }
 }
 
-TEST_F(CatapAudioInputStreamTest, LoopbackWithAllDevices) {
-  if (@available(macOS 14.2, *)) {
-    auto mock_catap_api_object = std::make_unique<MockCatapApi>();
-    // Keep a raw pointer to set expectations.
-    mock_catap_api_ = mock_catap_api_object.get();
-
-    // Create a CatapAudioInputStream for testing with
-    // kLoopbackWithMuteDeviceId.
-    stream_ = CreateCatapAudioInputStreamForTesting(
-        AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
-                        ChannelLayoutConfig::Stereo(), kLoopbackSampleRate,
-                        kCatapLoopbackDefaultFramesPerBuffer),
-        media::AudioDeviceDescription::kLoopbackAllDevicesId, base::DoNothing(),
-        base::DoNothing(), media::AudioDeviceDescription::kDefaultDeviceId,
-        std::move(mock_catap_api_object));
-    EXPECT_TRUE(stream_);
-
-    // Set up expectations for a successful open.
-    EXPECT_CALL(mock_catap_api(), AudioHardwareCreateProcessTap)
-        .WillOnce([](CATapDescription* in_description, AudioObjectID* out_tap) {
-          // Device UID and stream not set indicates that we're capturing all
-          // devices.
-          EXPECT_EQ([in_description deviceUID], nullptr);
-          EXPECT_EQ([in_description stream], nullptr);
-          *out_tap = kTap;
-          return noErr;
-        });
-
-    EXPECT_CALL(mock_catap_api(), AudioHardwareCreateAggregateDevice)
-        .WillOnce([](CFDictionaryRef in_device_properties,
-                     AudioDeviceID* out_device) {
-          *out_device = kAggregateDeviceId;
-          return noErr;
-        });
-    EXPECT_CALL(mock_catap_api(), AudioDeviceCreateIOProcID)
-        .WillOnce([this](AudioDeviceID in_device, AudioDeviceIOProc proc,
-                         void* in_client_data,
-                         AudioDeviceIOProcID* out_proc_id) {
-          EXPECT_EQ(in_device, kAggregateDeviceId);
-          audio_proc_ = proc;
-          EXPECT_EQ(in_client_data, stream_);
-          *out_proc_id = kTapIoProcId;
-          return noErr;
-        });
-
-    // Initialize the stream.
-    EXPECT_EQ(stream_->Open(), AudioInputStream::OpenOutcome::kSuccess);
-  }
-}
-
 }  // namespace media
diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc
index 4114098..5e007131 100644
--- a/media/audio/win/audio_low_latency_output_win.cc
+++ b/media/audio/win/audio_low_latency_output_win.cc
@@ -23,7 +23,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/bind_post_task.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/typed_macros.h"
 #include "base/win/scoped_propvariant.h"
 #include "media/audio/audio_device_description.h"
 #include "media/audio/win/audio_manager_win.h"
diff --git a/media/base/cdm_factory.h b/media/base/cdm_factory.h
index b82a601..c5a5e49 100644
--- a/media/base/cdm_factory.h
+++ b/media/base/cdm_factory.h
@@ -49,8 +49,8 @@
   kDisconnectionError = 13,
   // EME use is not allowed on unique origins.
   kNotAllowedOnUniqueOrigin = 14,
-  // Android: MediaDrmBridge creation failed.
-  kMediaDrmBridgeCreationFailed = 15,
+  // 15 was kMediaDrmBridgeCreationFailed; no longer used as we now use more
+  // detailed statuses.
   // Android: MediaCrypto not available.
   kMediaCryptoNotAvailable = 16,
   // CrOs: Only one instance allowed.
diff --git a/media/capture/video/chromeos/camera_buffer_factory.h b/media/capture/video/chromeos/camera_buffer_factory.h
index f1320ccf..99ec1ca 100644
--- a/media/capture/video/chromeos/camera_buffer_factory.h
+++ b/media/capture/video/chromeos/camera_buffer_factory.h
@@ -8,12 +8,12 @@
 #include <map>
 #include <memory>
 
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/capture/video/chromeos/mojom/camera3.mojom.h"
 #include "media/capture/video/chromeos/pixel_format_utils.h"
 #include "media/capture/video_capture_types.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/color_space.h"
+#include "ui/gfx/gpu_memory_buffer_handle.h"
 
 namespace gpu {
 class ClientSharedImage;
diff --git a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
index 2fce4d9d..13bca97 100644
--- a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
+++ b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
@@ -22,29 +22,10 @@
 // This class is designed as a singleton because it holds resources that needs
 // to be accessed globally. This ensures consistent state and minimizes memory
 // usage by sharing resources across components.
-class GpuResources : public gpu::GpuMemoryBufferManagerObserver {
+class GpuResources {
  public:
   GpuResources() = default;
 
-  void OnGpuMemoryBufferManagerDestroyed() override {
-    base::AutoLock lock(lock_);
-    // Invalidate the pointer to avoid dangling reference.
-    gpu_buffer_manager_ = nullptr;
-  }
-
-  gpu::GpuMemoryBufferManager* GetBufferManager() const {
-    base::AutoLock lock(lock_);
-    return gpu_buffer_manager_;
-  }
-
-  void SetBufferManager(gpu::GpuMemoryBufferManager* buffer_manager) {
-    base::AutoLock lock(lock_);
-    gpu_buffer_manager_ = buffer_manager;
-    if (buffer_manager) {
-      buffer_manager->AddObserver(this);
-    }
-  }
-
   scoped_refptr<gpu::SharedImageInterface> GetSharedImageInterface() const {
     base::AutoLock lock(lock_);
     return shared_image_interface_;
@@ -73,8 +54,6 @@
 
  private:
   mutable base::Lock lock_;
-  raw_ptr<gpu::GpuMemoryBufferManager> gpu_buffer_manager_ GUARDED_BY(lock_) =
-      nullptr;
   scoped_refptr<gpu::SharedImageInterface> shared_image_interface_
       GUARDED_BY(lock_);
   scoped_refptr<gpu::GpuChannelHost> gpu_channel_host_ GUARDED_BY(lock_);
diff --git a/media/gpu/chromeos/oop_video_decoder.cc b/media/gpu/chromeos/oop_video_decoder.cc
index 991d68a..29e7c8a 100644
--- a/media/gpu/chromeos/oop_video_decoder.cc
+++ b/media/gpu/chromeos/oop_video_decoder.cc
@@ -11,7 +11,6 @@
 #include "base/task/sequenced_task_runner.h"
 #include "build/build_config.h"
 #include "chromeos/components/cdm_factory_daemon/cdm_context_for_oopvd_impl.h"
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/format_utils.h"
 #include "media/base/video_util.h"
 #include "media/gpu/buffer_validation.h"
diff --git a/media/gpu/chromeos/vd_video_decode_accelerator.cc b/media/gpu/chromeos/vd_video_decode_accelerator.cc
index 5ef359e..0f8c3527 100644
--- a/media/gpu/chromeos/vd_video_decode_accelerator.cc
+++ b/media/gpu/chromeos/vd_video_decode_accelerator.cc
@@ -14,7 +14,6 @@
 #include "base/not_fatal_until.h"
 #include "base/task/sequenced_task_runner.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/format_utils.h"
 #include "media/base/media_util.h"
 #include "media/base/video_color_space.h"
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc
index fda5266..0125150 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -27,7 +27,6 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/format_utils.h"
 #include "media/base/media_log.h"
 #include "media/base/media_switches.h"
diff --git a/media/mojo/mojom/media_types.mojom b/media/mojo/mojom/media_types.mojom
index 108ff17..82428c5 100644
--- a/media/mojo/mojom/media_types.mojom
+++ b/media/mojo/mojom/media_types.mojom
@@ -675,7 +675,7 @@
   kUnsupportedKeySystem = 12,
   kDisconnectionError = 13,
   kNotAllowedOnUniqueOrigin = 14,
-  kMediaDrmBridgeCreationFailed = 15,
+  //kMediaDrmBridgeCreationFailed = 15,     // Deprecated
   kMediaCryptoNotAvailable = 16,
   kNoMoreInstances = 17,
   kInsufficientGpuResources = 18,
diff --git a/media/mojo/mojom/media_types_enum_mojom_traits.h b/media/mojo/mojom/media_types_enum_mojom_traits.h
index 88278d6..edb001b 100644
--- a/media/mojo/mojom/media_types_enum_mojom_traits.h
+++ b/media/mojo/mojom/media_types_enum_mojom_traits.h
@@ -521,8 +521,6 @@
         return media::mojom::CreateCdmStatus::kDisconnectionError;
       case media::CreateCdmStatus::kNotAllowedOnUniqueOrigin:
         return media::mojom::CreateCdmStatus::kNotAllowedOnUniqueOrigin;
-      case media::CreateCdmStatus::kMediaDrmBridgeCreationFailed:
-        return media::mojom::CreateCdmStatus::kMediaDrmBridgeCreationFailed;
       case media::CreateCdmStatus::kMediaCryptoNotAvailable:
         return media::mojom::CreateCdmStatus::kMediaCryptoNotAvailable;
       case media::CreateCdmStatus::kNoMoreInstances:
@@ -604,9 +602,6 @@
       case media::mojom::CreateCdmStatus::kNotAllowedOnUniqueOrigin:
         *output = media::CreateCdmStatus::kNotAllowedOnUniqueOrigin;
         return true;
-      case media::mojom::CreateCdmStatus::kMediaDrmBridgeCreationFailed:
-        *output = media::CreateCdmStatus::kMediaDrmBridgeCreationFailed;
-        return true;
       case media::mojom::CreateCdmStatus::kMediaCryptoNotAvailable:
         *output = media::CreateCdmStatus::kMediaCryptoNotAvailable;
         return true;
diff --git a/media/mojo/services/oop_video_decoder_service_unittest.cc b/media/mojo/services/oop_video_decoder_service_unittest.cc
index 93bc8ec..d465732 100644
--- a/media/mojo/services/oop_video_decoder_service_unittest.cc
+++ b/media/mojo/services/oop_video_decoder_service_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/posix/eintr_wrapper.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/mojo/common/media_type_converters.h"
 #include "media/mojo/common/mojo_decoder_buffer_converter.h"
 #include "media/mojo/mojom/media_log.mojom.h"
diff --git a/media/renderers/win/media_foundation_stream_wrapper.cc b/media/renderers/win/media_foundation_stream_wrapper.cc
index a3bd653..61420a6 100644
--- a/media/renderers/win/media_foundation_stream_wrapper.cc
+++ b/media/renderers/win/media_foundation_stream_wrapper.cc
@@ -8,7 +8,7 @@
 
 #include "base/functional/bind.h"
 #include "base/task/sequenced_task_runner.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "media/base/media_switches.h"
 #include "media/base/video_codecs.h"
 #include "media/base/win/mf_helpers.h"
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 4f9c88d..989dc44 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1787,6 +1787,8 @@
 
   if (enable_disk_cache_sql_backend) {
     sources += [
+      "disk_cache/sql/cache_entry_key.cc",
+      "disk_cache/sql/cache_entry_key.h",
       "disk_cache/sql/sql_backend_impl.cc",
       "disk_cache/sql/sql_backend_impl.h",
     ]
@@ -3490,7 +3492,10 @@
   }
 
   if (enable_disk_cache_sql_backend) {
-    sources += [ "disk_cache/sql/sql_backend_impl_unittest.cc" ]
+    sources += [
+      "disk_cache/sql/cache_entry_key_unittest.cc",
+      "disk_cache/sql/sql_backend_impl_unittest.cc",
+    ]
   }
 }
 
diff --git a/net/base/features.cc b/net/base/features.cc
index ace8fb63..348f9d0 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -303,6 +303,10 @@
                    "TcpPortRandomizationWinVersionMinimum",
                    static_cast<int>(base::win::Version::WIN10_20H1));
 
+BASE_FEATURE(kTcpPortReuseMetricsWin,
+             "TcpPortReuseMetricsWin",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 BASE_FEATURE(kTcpSocketIoCompletionPortWin,
              "TcpSocketIoCompletionPortWin",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/net/base/features.h b/net/base/features.h
index dae0829..89beac6 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -325,6 +325,10 @@
 NET_EXPORT BASE_DECLARE_FEATURE_PARAM(int,
                                       kTcpPortRandomizationWinVersionMinimum);
 
+// Whether or not TCP port reuse timing metrics are recorded.
+// See crbug.com/40744069 for more details.
+NET_EXPORT BASE_DECLARE_FEATURE(kTcpPortReuseMetricsWin);
+
 // Whether to use a TCP socket implementation which uses an IO completion
 // handler to be notified of completed reads and writes, instead of an event.
 NET_EXPORT BASE_DECLARE_FEATURE(kTcpSocketIoCompletionPortWin);
diff --git a/net/base/trace_constants.h b/net/base/trace_constants.h
index 0b77024..0460c7fe 100644
--- a/net/base/trace_constants.h
+++ b/net/base/trace_constants.h
@@ -5,7 +5,7 @@
 #ifndef NET_BASE_TRACE_CONSTANTS_H_
 #define NET_BASE_TRACE_CONSTANTS_H_
 
-#include "base/trace_event/common/trace_event_common.h"
+#include "base/trace_event/trace_event.h"
 
 namespace net {
 
diff --git a/net/base/tracing.h b/net/base/tracing.h
index 340f2fbb..4b9f7a9c 100644
--- a/net/base/tracing.h
+++ b/net/base/tracing.h
@@ -5,9 +5,8 @@
 #ifndef NET_BASE_TRACING_H_
 #define NET_BASE_TRACING_H_
 
+#include "base/trace_event/trace_event.h"  // IWYU pragma: export
 #include "build/build_config.h"
 #include "net/base/cronet_buildflags.h"
 
-#include "base/trace_event/base_tracing.h"  // IWYU pragma: export
-
 #endif  // NET_BASE_TRACING_H_
diff --git a/net/disk_cache/sql/cache_entry_key.cc b/net/disk_cache/sql/cache_entry_key.cc
new file mode 100644
index 0000000..575d114
--- /dev/null
+++ b/net/disk_cache/sql/cache_entry_key.cc
@@ -0,0 +1,34 @@
+// 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 "net/disk_cache/sql/cache_entry_key.h"
+
+#include "base/check.h"
+
+namespace disk_cache {
+
+CacheEntryKey::CacheEntryKey(std::string str)
+    : data_(base::MakeRefCounted<base::RefCountedString>(std::move(str))) {}
+
+CacheEntryKey::~CacheEntryKey() = default;
+
+CacheEntryKey::CacheEntryKey(const CacheEntryKey& other) = default;
+CacheEntryKey::CacheEntryKey(CacheEntryKey&& other) = default;
+CacheEntryKey& CacheEntryKey::operator=(const CacheEntryKey& other) = default;
+CacheEntryKey& CacheEntryKey::operator=(CacheEntryKey&& other) = default;
+
+bool CacheEntryKey::operator<(const CacheEntryKey& other) const {
+  return data_ != other.data_ && string() < other.string();
+}
+
+bool CacheEntryKey::operator==(const CacheEntryKey& other) const {
+  return data_ == other.data_ || string() == other.string();
+}
+
+const std::string& CacheEntryKey::string() const {
+  DCHECK(data_);
+  return data_->as_string();
+}
+
+}  // namespace disk_cache
diff --git a/net/disk_cache/sql/cache_entry_key.h b/net/disk_cache/sql/cache_entry_key.h
new file mode 100644
index 0000000..97810b6f
--- /dev/null
+++ b/net/disk_cache/sql/cache_entry_key.h
@@ -0,0 +1,71 @@
+// 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 NET_DISK_CACHE_SQL_CACHE_ENTRY_KEY_H_
+#define NET_DISK_CACHE_SQL_CACHE_ENTRY_KEY_H_
+
+#include <optional>
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_memory.h"
+#include "net/base/net_export.h"
+
+namespace disk_cache {
+
+// Represents the key for a cache entry in the SQL disk cache backend.
+//
+// This class is a wrapper around the cache key string, which is generated by
+// HttpCache::GenerateCacheKeyForRequest(). These keys can be long, and the
+// SQL backend uses them as keys in multiple in-memory maps (e.g., for tracking
+// active, doomed, and recently used entries). The key is also passed between
+// threads for database operations.
+//
+// To avoid high memory consumption from duplicating these long strings, this
+// class holds the key in a `scoped_refptr<base::RefCountedString>`. This
+// allows multiple data structures to share the same underlying string data
+// cheaply, reducing overall memory usage.
+//
+// The class provides comparison operators and a `std::hash` specialization so
+// it can be used efficiently as a key in both ordered and unordered STL
+// containers.
+//
+// Future Work: For the Renderer-Accessible HTTP Cache project, this class is
+// expected to be extended to also hold a cache isolation key, in addition to
+// the main cache key string.
+class NET_EXPORT_PRIVATE CacheEntryKey {
+ public:
+  explicit CacheEntryKey(std::string str = "");
+  ~CacheEntryKey();
+
+  CacheEntryKey(const CacheEntryKey& other);
+  CacheEntryKey(CacheEntryKey&& other);
+  CacheEntryKey& operator=(const CacheEntryKey& other);
+  CacheEntryKey& operator=(CacheEntryKey&& other);
+
+  bool operator<(const CacheEntryKey& other) const;
+  bool operator==(const CacheEntryKey& other) const;
+
+  const std::string& string() const;
+
+ private:
+  scoped_refptr<const base::RefCountedString> data_;
+};
+
+}  // namespace disk_cache
+
+namespace std {
+
+// Implement hashing of CacheEntryKey, so it can be used as key in STL
+// containers.
+template <>
+struct hash<disk_cache::CacheEntryKey> {
+  std::size_t operator()(const disk_cache::CacheEntryKey& k) const {
+    return std::hash<std::string>{}(k.string());
+  }
+};
+
+}  // namespace std
+
+#endif  // NET_DISK_CACHE_SQL_CACHE_ENTRY_KEY_H_
diff --git a/net/disk_cache/sql/cache_entry_key_unittest.cc b/net/disk_cache/sql/cache_entry_key_unittest.cc
new file mode 100644
index 0000000..cdd31f6be
--- /dev/null
+++ b/net/disk_cache/sql/cache_entry_key_unittest.cc
@@ -0,0 +1,146 @@
+// 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 "net/disk_cache/sql/cache_entry_key.h"
+
+#include <string>
+#include <unordered_set>
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace disk_cache {
+namespace {
+
+TEST(CacheEntryKeyTest, DefaultConstructor) {
+  CacheEntryKey key;
+  EXPECT_EQ(key.string(), "");
+}
+
+TEST(CacheEntryKeyTest, ValueConstructor) {
+  const std::string kValue = "my_key";
+  CacheEntryKey key(kValue);
+  EXPECT_EQ(key.string(), kValue);
+}
+
+TEST(CacheEntryKeyTest, CopyConstructor) {
+  const std::string kValue = "my_key";
+  CacheEntryKey key1(kValue);
+  CacheEntryKey key2(key1);
+
+  EXPECT_EQ(key1.string(), kValue);
+  EXPECT_EQ(key2.string(), kValue);
+  // The copy should be equal, and this also tests the fast-path where the
+  // underlying RefCountedString pointers are identical.
+  EXPECT_EQ(key1, key2);
+}
+
+TEST(CacheEntryKeyTest, MoveConstructor) {
+  const std::string kValue = "my_key";
+  CacheEntryKey key1(kValue);
+  CacheEntryKey key2(std::move(key1));
+
+  // The moved-to key should have the original value.
+  EXPECT_EQ(key2.string(), kValue);
+}
+
+TEST(CacheEntryKeyTest, CopyAssignment) {
+  const std::string kValue1 = "key1";
+  const std::string kValue2 = "key2";
+  CacheEntryKey key1(kValue1);
+  CacheEntryKey key2(kValue2);
+
+  EXPECT_NE(key1, key2);
+
+  key2 = key1;
+  EXPECT_EQ(key1.string(), kValue1);
+  EXPECT_EQ(key2.string(), kValue1);
+  EXPECT_EQ(key1, key2);
+}
+
+TEST(CacheEntryKeyTest, MoveAssignment) {
+  const std::string kValue1 = "key1";
+  const std::string kValue2 = "key2";
+  CacheEntryKey key1(kValue1);
+  CacheEntryKey key2(kValue2);
+
+  EXPECT_NE(key1, key2);
+
+  key2 = std::move(key1);
+  // The moved-to key should have the original value.
+  EXPECT_EQ(key2.string(), kValue1);
+}
+
+TEST(CacheEntryKeyTest, ComparisonOperators) {
+  CacheEntryKey key_a("a");
+  CacheEntryKey key_a_copy("a");
+  CacheEntryKey key_b("b");
+  CacheEntryKey empty_key("");
+
+  // Operator==
+  EXPECT_EQ(key_a, key_a_copy);
+  EXPECT_FALSE(key_a == key_b);
+  EXPECT_FALSE(key_a == empty_key);
+  EXPECT_EQ(CacheEntryKey(), empty_key);
+
+  // Operator<
+  EXPECT_LT(key_a, key_b);
+  EXPECT_LT(empty_key, key_a);
+  EXPECT_FALSE(key_b < key_a);
+  EXPECT_FALSE(key_a < key_a);
+  EXPECT_FALSE(key_a < key_a_copy);
+}
+
+TEST(CacheEntryKeyTest, LessThanOperatorFastPath) {
+  // 1. Test with two keys sharing the exact same underlying data object.
+  // The new `data_ != other.data_` check should short-circuit to false.
+  const std::string kValue = "my_key";
+  CacheEntryKey key1(kValue);
+  CacheEntryKey key2(key1);
+
+  ASSERT_EQ(key1, key2);
+  // An object cannot be less than itself.
+  EXPECT_FALSE(key1 < key2);
+  EXPECT_FALSE(key2 < key1);
+
+  // 2. Test with two keys having different data objects but the same string
+  // value. The `data_ != other.data_` check passes, but the string
+  // comparison 'kValue < kValue' is correctly false.
+  CacheEntryKey key3(kValue);
+
+  ASSERT_EQ(key1, key3);
+  // The pointers are different, but the strings are equal.
+  EXPECT_FALSE(key1 < key3);
+  EXPECT_FALSE(key3 < key1);
+}
+
+TEST(CacheEntryKeyTest, StdHash) {
+  std::unordered_set<CacheEntryKey> key_set;
+
+  CacheEntryKey key1("key1");
+  CacheEntryKey key2("key2");
+  CacheEntryKey key1_copy("key1");
+
+  // Insert keys.
+  auto result1 = key_set.insert(key1);
+  EXPECT_TRUE(result1.second);  // Insertion should succeed.
+  EXPECT_EQ(key_set.size(), 1u);
+
+  auto result2 = key_set.insert(key2);
+  EXPECT_TRUE(result2.second);
+  EXPECT_EQ(key_set.size(), 2u);
+
+  // Try inserting a duplicate.
+  auto result3 = key_set.insert(key1_copy);
+  EXPECT_FALSE(result3.second);  // Insertion should fail.
+  EXPECT_EQ(key_set.size(), 2u);
+
+  // Find keys.
+  EXPECT_EQ(key_set.count(key1), 1u);
+  EXPECT_EQ(key_set.count(key2), 1u);
+  EXPECT_EQ(key_set.count(key1_copy), 1u);
+  EXPECT_EQ(key_set.count(CacheEntryKey("non_existent_key")), 0u);
+}
+
+}  // namespace
+}  // namespace disk_cache
diff --git a/net/log/net_log_util.cc b/net/log/net_log_util.cc
index 1b86631..6bd306f 100644
--- a/net/log/net_log_util.cc
+++ b/net/log/net_log_util.cc
@@ -17,7 +17,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"  // IWYU pragma: export
+#include "base/trace_event/trace_event.h"  // IWYU pragma: export
 #include "base/values.h"
 #include "net/base/address_family.h"
 #include "net/base/load_states.h"
diff --git a/net/log/net_log_util.h b/net/log/net_log_util.h
index 2d8f50bf..5b013dee93 100644
--- a/net/log/net_log_util.h
+++ b/net/log/net_log_util.h
@@ -8,7 +8,7 @@
 #include <memory>
 #include <set>
 
-#include "base/trace_event/base_tracing.h"  // IWYU pragma: export
+#include "base/trace_event/trace_event.h"  // IWYU pragma: export
 #include "net/base/net_export.h"
 #include "net/log/net_log.h"
 
diff --git a/net/socket/tcp_socket_win.cc b/net/socket/tcp_socket_win.cc
index af9708d..bed3071 100644
--- a/net/socket/tcp_socket_win.cc
+++ b/net/socket/tcp_socket_win.cc
@@ -17,7 +17,9 @@
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/notimplemented.h"
+#include "base/strings/string_util.h"
 #include "base/win/windows_version.h"
 #include "net/base/address_list.h"
 #include "net/base/features.h"
@@ -107,6 +109,75 @@
                  features::kTcpPortRandomizationWinVersionMinimum.Get());
 }
 
+// [crbug.com/40744069] This function and the two below are used to track
+// metrics on port reuse so that when SO_RANDOMIZE_PORT is enabled on windows
+// we will know if the distribution skews in a way likely to cause errors.
+std::map<std::string, base::Time>& GetLocalEndPointToLastSocketCloseTimeMap() {
+  static base::NoDestructor<std::map<std::string, base::Time>> map;
+  return *map;
+}
+
+// See comment on GetLocalEndPointToLastSocketCloseTimeMap.
+void RecordSocketConnectForReuseMetrics(TCPSocketWin* socket, bool success) {
+  if (!base::FeatureList::IsEnabled(features::kTcpPortReuseMetricsWin)) {
+    return;
+  }
+  net::IPEndPoint local_address;
+  int net_error = socket->GetLocalAddress(&local_address);
+  if (net_error != net::OK || local_address.address().IsZero() ||
+      local_address.port() == 0) {
+    return;
+  }
+  base::Time last_closed_after_successful_open;
+  if (success) {
+    // If the port was successfully opened, then we should clear the last closed
+    // time and wait for it to be closed again.
+    const auto node = GetLocalEndPointToLastSocketCloseTimeMap().extract(
+        local_address.ToString());
+    if (!node) {
+      return;
+    }
+    last_closed_after_successful_open = node.mapped();
+  } else {
+    // If the port was not successfully opened, then we should just read the
+    // last closed time without clearing it so we can measure from last success.
+    const auto it = GetLocalEndPointToLastSocketCloseTimeMap().find(
+        local_address.ToString());
+    if (it == GetLocalEndPointToLastSocketCloseTimeMap().end()) {
+      return;
+    }
+    last_closed_after_successful_open = it->second;
+  }
+  std::string ip_address_type = "Other";
+  if (local_address.address().IsLoopback()) {
+    ip_address_type = "Loopback";
+  } else if (local_address.address().IsLinkLocal()) {
+    ip_address_type = "LinkLocal";
+  }
+  base::UmaHistogramTimes(
+      base::JoinString({"Net.TCPSocket.PortReuseTimeWindows", ip_address_type,
+                        success ? "Success" : "Failure"},
+                       "."),
+      base::Time::Now() - last_closed_after_successful_open);
+}
+
+// See comment on GetLocalEndPointToLastSocketCloseTimeMap.
+void RecordSocketCloseForReuseMetrics(TCPSocketWin* socket) {
+  if (!base::FeatureList::IsEnabled(features::kTcpPortReuseMetricsWin)) {
+    return;
+  }
+  net::IPEndPoint local_address;
+  int net_error = socket->GetLocalAddress(&local_address);
+  if (net_error != net::OK || local_address.address().IsZero() ||
+      local_address.port() == 0) {
+    return;
+  }
+  // If the map already contains an entry for `local_address` then the last open
+  // was unsuccessful and we should reuse the last close time instead.
+  GetLocalEndPointToLastSocketCloseTimeMap().try_emplace(
+      local_address.ToString(), base::Time::Now());
+}
+
 }  // namespace
 
 //-----------------------------------------------------------------------------
@@ -807,6 +878,9 @@
     // Only log the close event if there's actually a socket to close.
     net_log_.AddEvent(NetLogEventType::SOCKET_CLOSED);
 
+    // [crbug.com/40744069] Log port reuse metrics.
+    RecordSocketCloseForReuseMetrics(this);
+
     // Note: don't use CancelIo to cancel pending IO because it doesn't work
     // when there is a Winsock layered service provider.
 
@@ -1015,6 +1089,9 @@
     net_log_.EndEvent(NetLogEventType::TCP_CONNECT_ATTEMPT);
   }
 
+  // [crbug.com/40744069] Log port reuse metrics.
+  RecordSocketConnectForReuseMetrics(this, /*success=*/result == OK);
+
   if (!logging_multiple_connect_attempts_)
     LogConnectEnd(result);
 }
diff --git a/net/third_party/quiche/src b/net/third_party/quiche/src
index 35b5350..f0a8dce 160000
--- a/net/third_party/quiche/src
+++ b/net/third_party/quiche/src
@@ -1 +1 @@
-Subproject commit 35b5350ab0afe83a60dbf23ac0f4585e901db7f8
+Subproject commit f0a8dcea08196573119875267b4f8303241a24d9
diff --git a/services/on_device_model/on_device_model_service_unittest.cc b/services/on_device_model/on_device_model_service_unittest.cc
index 7cacebe..70849fe2 100644
--- a/services/on_device_model/on_device_model_service_unittest.cc
+++ b/services/on_device_model/on_device_model_service_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
+#include "base/threading/thread_restrictions.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/on_device_model/fake/fake_chrome_ml_api.h"
 #include "services/on_device_model/fake/on_device_model_fake.h"
diff --git a/services/tracing/public/cpp/background_tracing/background_tracing_agent_impl.cc b/services/tracing/public/cpp/background_tracing/background_tracing_agent_impl.cc
index bbe29ea..e8f341cc 100644
--- a/services/tracing/public/cpp/background_tracing/background_tracing_agent_impl.cc
+++ b/services/tracing/public/cpp/background_tracing/background_tracing_agent_impl.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/statistics_recorder.h"
 #include "base/trace_event/histogram_scope.h"
 #include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_id_helper.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "services/tracing/public/cpp/perfetto/macros.h"
 #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_histogram_sample.pbzero.h"
diff --git a/services/viz/privileged/mojom/gl/BUILD.gn b/services/viz/privileged/mojom/gl/BUILD.gn
index 0c4ce5c..991caeaa 100644
--- a/services/viz/privileged/mojom/gl/BUILD.gn
+++ b/services/viz/privileged/mojom/gl/BUILD.gn
@@ -10,6 +10,7 @@
   sources = [
     "context_lost_reason.mojom",
     "gpu_host.mojom",
+    "gpu_logging.mojom",
     "gpu_service.mojom",
   ]
 
diff --git a/services/viz/privileged/mojom/gl/gpu_host.mojom b/services/viz/privileged/mojom/gl/gpu_host.mojom
index 6030596..ac72fdb8 100644
--- a/services/viz/privileged/mojom/gl/gpu_host.mojom
+++ b/services/viz/privileged/mojom/gl/gpu_host.mojom
@@ -73,8 +73,6 @@
                   mojo_base.mojom.ByteString key,
                   mojo_base.mojom.ByteString blob);
 
-  RecordLogMessage(int32 severity, string header, string message);
-
   // Tells the GPU host to clear the shader disk cache.
   ClearGrShaderDiskCache();
 };
diff --git a/services/viz/privileged/mojom/gl/gpu_logging.mojom b/services/viz/privileged/mojom/gl/gpu_logging.mojom
new file mode 100644
index 0000000..be64dca
--- /dev/null
+++ b/services/viz/privileged/mojom/gl/gpu_logging.mojom
@@ -0,0 +1,15 @@
+// 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 viz.mojom;
+
+// This is used for sending GPU process log messages to the browser process.
+// This mojom file is extracted from gpu_host.mojom, to have a separate mojo
+// channel to handle log messages. The Remote side is running on GPU IO thread.
+// When GPU context is lost, terminate GPU process on IO thread to make sure
+// GPU log messages are sent to browser process as much as possible.
+interface GpuLogging {
+  // Record GPU process log messages. The messages are shown on chrome://gpu.
+  RecordLogMessage(int32 severity, string header, string message);
+};
diff --git a/services/viz/privileged/mojom/viz_main.mojom b/services/viz/privileged/mojom/viz_main.mojom
index e0002e50..6650895a 100644
--- a/services/viz/privileged/mojom/viz_main.mojom
+++ b/services/viz/privileged/mojom/viz_main.mojom
@@ -12,6 +12,7 @@
 import "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom";
 import "services/viz/privileged/mojom/compositing/renderer_settings.mojom";
 import "services/viz/privileged/mojom/gl/gpu_host.mojom";
+import "services/viz/privileged/mojom/gl/gpu_logging.mojom";
 [EnableIf=is_win]
 import "services/viz/privileged/mojom/gl/info_collection_gpu_service.mojom";
 import "services/viz/privileged/mojom/gl/gpu_service.mojom";
@@ -52,6 +53,7 @@
   CreateGpuService(
       pending_receiver<GpuService> gpu_service,
       pending_remote<GpuHost> gpu_host,
+      pending_remote<GpuLogging> gpu_logging,
       pending_remote<discardable_memory.mojom.DiscardableSharedMemoryManager>
           discardable_memory_manager,
       mojo_base.mojom.UnsafeSharedMemoryRegion? use_shader_cache_shm_count,
diff --git a/sql/statement.cc b/sql/statement.cc
index 4648ef4..52e8270 100644
--- a/sql/statement.cc
+++ b/sql/statement.cc
@@ -34,7 +34,7 @@
 #include "base/threading/scoped_blocking_call.h"
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "sql/database.h"
 #include "sql/sqlite_result_code.h"
 #include "sql/sqlite_result_code_values.h"
diff --git a/storage/browser/quota/quota_manager_unittest.cc b/storage/browser/quota/quota_manager_unittest.cc
index 6d1d8f3..d5b6b4b 100644
--- a/storage/browser/quota/quota_manager_unittest.cc
+++ b/storage/browser/quota/quota_manager_unittest.cc
@@ -33,6 +33,7 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "components/services/storage/public/cpp/buckets/bucket_info.h"
 #include "components/services/storage/public/cpp/buckets/bucket_locator.h"
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index bad25f7..d72e37a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3164,29 +3164,6 @@
             ]
         }
     ],
-    "BackgroundUpdateForRegisteredStorageKeys": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "chromeos_lacros",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "BackgroundUpdateForRegisteredStorageKeys",
-                    "enable_features": [
-                        "ServiceWorkerBackgroundUpdateForRegisteredStorageKeys",
-                        "ServiceWorkerBackgroundUpdateForRegisteredStorageKeysFinchControlled"
-                    ]
-                }
-            ]
-        }
-    ],
     "BatchNativeEventsInMessagePumpEpoll": [
         {
             "platforms": [
@@ -7397,6 +7374,26 @@
             ]
         }
     ],
+    "DefaultSiteInstanceGroups": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DefaultSiteInstanceGroups"
+                    ]
+                }
+            ]
+        }
+    ],
     "DeferConciergeStartup": [
         {
             "platforms": [
@@ -22415,6 +22412,42 @@
             ]
         }
     ],
+    "ServiceWorkerBackgroundUpdateForRegisteredStorageKeys": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "ServiceWorkerBackgroundUpdateForRegisteredStorageKeys",
+                    "enable_features": [
+                        "ServiceWorkerBackgroundUpdateForRegisteredStorageKeys"
+                    ]
+                }
+            ]
+        }
+    ],
+    "ServiceWorkerBackgroundUpdateForRegisteredStorageKeysForWebView": [
+        {
+            "platforms": [
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "ServiceWorkerBackgroundUpdateForRegisteredStorageKeysForWebView",
+                    "enable_features": [
+                        "ServiceWorkerBackgroundUpdateForRegisteredStorageKeysFieldTrialControlled"
+                    ]
+                }
+            ]
+        }
+    ],
     "ServiceWorkerStaticRouterRaceNetworkRequestPerformanceImprovement": [
         {
             "platforms": [
diff --git a/third_party/angle b/third_party/angle
index f6b40bc..db96240 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit f6b40bc8397af944cb09ecb606b2d809466a5f02
+Subproject commit db9624073324a2e5b485df7ba24c4cd45d9dceab
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index b39b3e7..8d612733 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -4700,6 +4700,11 @@
         protanopia
         tritanopia
 
+  # Emulates the given OS text scale.
+  command setEmulatedOSTextScale
+    parameters
+      optional number scale
+
   # Overrides the Geolocation Position or Error. Omitting latitude, longitude or
   # accuracy emulates position unavailable.
   command setGeolocationOverride
diff --git a/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom b/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom
index 455ac51..44cb7b66 100644
--- a/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom
+++ b/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom
@@ -38,16 +38,23 @@
   // language pair is not supported.
   kNoNotSupportedLanguage = 5,
 
+  // The translator cannot be created because the Accept-Language check failed.
+  kNoAcceptLanguagesCheckFailed = 6,
+
+  // The translator cannot be created. New models need to be downloaded,
+  // but the number of downloaded language packs will exceedd the limit.
+  kNoExceedsLanguagePackCountLimitation = 7,
+
   // The translator cannot be created, because the translator service crashed.
-  kNoServiceCrashed = 6,
+  kNoServiceCrashed = 8,
 
   // The translator cannot be created, because the use of Translator API is
   // disallowed by `TranslatorAPIAllowed` Enterprise policy
-  kNoDisallowedByPolicy = 7,
+  kNoDisallowedByPolicy = 9,
 
   // The translator cannot be created, because the number of services exceeds
   // the limitation.
-  kNoExceedsServiceCountLimitation = 8,
+  kNoExceedsServiceCountLimitation = 10,
 };
 
 // The error of TranslationManager's CreateTranslator IPC.
@@ -67,23 +74,30 @@
   // translator.
   kFailedToCreateTranslator = 4,
 
+  // The translator cannot be created because the Accept-Language check failed.
+  kAcceptLanguagesCheckFailed = 5,
+
+  // The translator cannot be created. New models need to be downloaded,
+  // but the number of downloaded language packs will exceedd the limit.
+  kExceedsLanguagePackCountLimitation = 6,
+
   // The translator cannot be created, because the translator service crashed.
-  kServiceCrashed = 5,
+  kServiceCrashed = 7,
 
   // The translator cannot be created, because the use of Translator API is
   // disallowed by `TranslatorAPIAllowed` Enterprise policy
-  kDisallowedByPolicy = 6,
+  kDisallowedByPolicy = 8,
 
   // The translator cannot be created, because the number of services exceeds
   // the limitation.
-  kExceedsServiceCountLimitation = 7,
+  kExceedsServiceCountLimitation = 9,
 
   // The translator cannot be created, because the number of pending tasks
   // exceeds the limitation.
-  kExceedsPendingTaskCountLimitation =  8,
+  kExceedsPendingTaskCountLimitation = 10,
 
   // The translator cannot be created because the library version is invalid.
-  kInvalidVersion = 9,
+  kInvalidVersion = 11,
 };
 
 // The result of TranslationManager's CreateTranslator IPC.
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.cc b/third_party/blink/renderer/core/exported/web_settings_impl.cc
index 90692da7..3d700ee 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc
@@ -161,8 +161,9 @@
   dev_tools_emulator_->SetTextAutosizingEnabled(enabled);
 }
 
+// TODO(pdr): Rename this OSTextScaleFactor.
 void WebSettingsImpl::SetAccessibilityFontScaleFactor(float font_scale_factor) {
-  settings_->SetAccessibilityFontScaleFactor(font_scale_factor);
+  dev_tools_emulator_->SetAccessibilityFontScaleFactor(font_scale_factor);
 }
 
 void WebSettingsImpl::SetAccessibilityTextSizeContrastFactor(
diff --git a/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc b/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc
index 066b050..a40102a 100644
--- a/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc
+++ b/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/core/frame/animation_frame_timing_monitor.h"
 
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "components/viz/common/frame_timing_details.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
diff --git a/third_party/blink/renderer/core/html/anchor_element_metrics_sender_test.cc b/third_party/blink/renderer/core/html/anchor_element_metrics_sender_test.cc
index 4a1b4c4..82497f2 100644
--- a/third_party/blink/renderer/core/html/anchor_element_metrics_sender_test.cc
+++ b/third_party/blink/renderer/core/html/anchor_element_metrics_sender_test.cc
@@ -1030,7 +1030,7 @@
   constexpr gfx::Vector2dF velocity{20, 20};
   constexpr base::TimeDelta timestep = base::Milliseconds(20);
   for (base::TimeDelta t;
-       t <= 2 * AnchorElementInteractionTracker::GetHoverDwellTime();
+       t <= 2 * AnchorElementInteractionTracker::kModerateHoverDwellTime;
        t += timestep) {
     gfx::PointF coordinates =
         origin + gfx::ScaleVector2d(velocity, t.InSecondsF());
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
index 9bf1156..2e8a2c7 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
@@ -146,11 +146,6 @@
 CanvasResourceProvider*
 CanvasRenderingContextHost::GetOrCreateCanvasResourceProviderForCanvas2D() {
   CHECK(IsRenderingContext2D());
-
-  // NOTE: When further reorganizing this code, it is important to preserve the
-  // flow of HTMLCanvasElement overriding
-  // GetOrCreateCanvasResourceProviderImpl() to customize the creation of
-  // Canvas2D resource providers.
   return GetOrCreateCanvasResourceProviderImpl();
 }
 
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
index ffa01b6..6fce9f99 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
@@ -148,7 +148,7 @@
 
   scoped_refptr<StaticBitmapImage> CreateTransparentImage() const;
 
-  CanvasResourceProvider* GetOrCreateCanvasResourceProviderImpl() override;
+  CanvasResourceProvider* GetOrCreateCanvasResourceProviderImpl() final;
 
   bool ContextHasOpenLayers(const CanvasRenderingContext*) const;
 
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index f410bf9..51a7932 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -2218,7 +2218,7 @@
 
   // Bail out if it's not possible to create a new provider.
   CanvasResourceProvider* new_provider =
-      RecreateCanvasResourceProviderFor2DContext(
+      RecreateCanvasResourceProviderForCanvas2D(
           CHECK_DEREF(hibernation_handler_.get()));
   if (!new_provider) {
     return;
@@ -2231,69 +2231,67 @@
 }
 
 CanvasResourceProvider*
-HTMLCanvasElement::GetOrCreateCanvasResourceProviderImpl() {
-  if (IsRenderingContext2D()) {
-    CanvasResourceProvider* resource_provider =
-        GetResourceProviderForCanvas2D();
-    if (context_->isContextLost() && !context_->IsContextBeingRestored()) {
-      DCHECK(!resource_provider);
+HTMLCanvasElement::GetOrCreateCanvasResourceProviderForCanvas2D() {
+  CHECK(IsRenderingContext2D());
+  CanvasResourceProvider* resource_provider = GetResourceProviderForCanvas2D();
+  if (context_->isContextLost() && !context_->IsContextBeingRestored()) {
+    DCHECK(!resource_provider);
+    return nullptr;
+  }
+
+  if (resource_provider) {
+    if (!resource_provider->IsValid()) {
+      // The canvas context is not lost but the provider is invalid. This
+      // happens if the GPU process dies in the middle of a render task. The
+      // canvas is notified of GPU context losses via the
+      // `NotifyGpuContextLost` callback and restoration happens in
+      // `TryRestoreContextEvent`. Both callbacks are executed in their own
+      // separate task. If the GPU context goes invalid in the middle of a
+      // render task, the canvas won't immediately know about it and canvas
+      // APIs will continue using the provider that is now invalid. We can
+      // early return here, trying to re-create the provider right away would
+      // just fail. We need to let `TryRestoreContextEvent` wait for the GPU
+      // process to up again.
       return nullptr;
     }
-
-    if (resource_provider) {
-      if (!resource_provider->IsValid()) {
-        // The canvas context is not lost but the provider is invalid. This
-        // happens if the GPU process dies in the middle of a render task. The
-        // canvas is notified of GPU context losses via the
-        // `NotifyGpuContextLost` callback and restoration happens in
-        // `TryRestoreContextEvent`. Both callbacks are executed in their own
-        // separate task. If the GPU context goes invalid in the middle of a
-        // render task, the canvas won't immediately know about it and canvas
-        // APIs will continue using the provider that is now invalid. We can
-        // early return here, trying to re-create the provider right away would
-        // just fail. We need to let `TryRestoreContextEvent` wait for the GPU
-        // process to up again.
-        return nullptr;
-      }
-      return resource_provider;
-    }
-
-    if (did_fail_to_create_resource_provider_) {
-      return nullptr;
-    }
-
-    if (!IsValidImageSize()) {
-      did_fail_to_create_resource_provider_ = true;
-      if (!Size().IsEmpty() && context_) {
-        context_->LoseContext(CanvasRenderingContext::kInvalidCanvasSize);
-      }
-      return nullptr;
-    }
-
-    UpdatePreferred2DRasterMode();
-
-    if (!hibernation_handler_) {
-      hibernation_handler_ = std::make_unique<CanvasHibernationHandler>(*this);
-    }
-
-    resource_provider = RecreateCanvasResourceProviderFor2DContext(
-        CHECK_DEREF(hibernation_handler_.get()));
-
-    UpdateMemoryUsage();
-
-    if (context_) {
-      SetNeedsCompositingUpdate();
-    }
-
     return resource_provider;
   }
 
-  return CanvasRenderingContextHost::GetOrCreateCanvasResourceProviderImpl();
+  if (did_fail_to_create_resource_provider_) {
+    return nullptr;
+  }
+
+  if (!IsValidImageSize()) {
+    did_fail_to_create_resource_provider_ = true;
+    if (!Size().IsEmpty() && context_) {
+      context_->LoseContext(CanvasRenderingContext::kInvalidCanvasSize);
+    }
+    return nullptr;
+  }
+
+  UpdatePreferred2DRasterMode();
+
+  if (!hibernation_handler_) {
+    hibernation_handler_ = std::make_unique<CanvasHibernationHandler>(*this);
+  }
+
+  resource_provider = RecreateCanvasResourceProviderForCanvas2D(
+      CHECK_DEREF(hibernation_handler_.get()));
+
+  UpdateMemoryUsage();
+
+  if (context_) {
+    SetNeedsCompositingUpdate();
+  }
+
+  return resource_provider;
 }
 
 CanvasResourceProvider*
-HTMLCanvasElement::RecreateCanvasResourceProviderFor2DContext(
+HTMLCanvasElement::RecreateCanvasResourceProviderForCanvas2D(
     CanvasHibernationHandler& hibernation_handler) {
+  CHECK(IsRenderingContext2D());
+
   // We call GetOrCreateCanvasResourceProviderImpl directly here to prevent a
   // circular callstack.
   CanvasResourceProvider* resource_provider =
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
index 8a75c71..f59e2b2f 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
@@ -260,7 +260,8 @@
   size_t GetMemoryUsage() const override;
   bool ShouldAccelerate2dContext() const override;
   bool LowLatencyEnabled() const override;
-  CanvasResourceProvider* GetOrCreateCanvasResourceProviderImpl() override;
+  CanvasResourceProvider* GetOrCreateCanvasResourceProviderForCanvas2D()
+      override;
   bool IsPrinting() const override;
   bool IsHibernating() const override;
   void SetTransferToGPUTextureWasInvoked() override;
@@ -383,7 +384,7 @@
   // Recreates the resource provider.
   // TODO(crbug.com/40280152): Remove parameter once the hibernation handler is
   // an instance variable of this class.
-  CanvasResourceProvider* RecreateCanvasResourceProviderFor2DContext(
+  CanvasResourceProvider* RecreateCanvasResourceProviderForCanvas2D(
       CanvasHibernationHandler& hibernation_handler);
 
   void ColorSchemeMayHaveChanged();
diff --git a/third_party/blink/renderer/core/inspector/dev_tools_emulator.cc b/third_party/blink/renderer/core/inspector/dev_tools_emulator.cc
index 48db1f880..f26c29e 100644
--- a/third_party/blink/renderer/core/inspector/dev_tools_emulator.cc
+++ b/third_party/blink/renderer/core/inspector/dev_tools_emulator.cc
@@ -161,7 +161,10 @@
       document_cookie_disabled_(false),
       embedder_force_dark_mode_enabled_(
           web_view->GetPage()->GetSettings().GetForceDarkModeEnabled()),
-      auto_dark_overriden_(false) {}
+      auto_dark_overriden_(false),
+      embedder_accessibility_font_scale_(
+          web_view->GetPage()->GetSettings().GetAccessibilityFontScaleFactor()),
+      accessibility_font_scale_emulation_enabled_(false) {}
 
 DevToolsEmulator::~DevToolsEmulator() {
   // This class is GarbageCollected, so desturctor may run at any time, hence
@@ -589,4 +592,28 @@
   }
 }
 
+void DevToolsEmulator::SetAccessibilityFontScaleFactor(double scale) {
+  embedder_accessibility_font_scale_ = scale;
+  if (!accessibility_font_scale_emulation_enabled_) {
+    web_view_->GetPage()->GetSettings().SetAccessibilityFontScaleFactor(scale);
+  }
+}
+
+void DevToolsEmulator::SetEmulatedAccessibilityFontScaleFactor(double scale) {
+  if (!accessibility_font_scale_emulation_enabled_) {
+    accessibility_font_scale_emulation_enabled_ = true;
+    embedder_accessibility_font_scale_ =
+        web_view_->GetPage()->GetSettings().GetAccessibilityFontScaleFactor();
+  }
+  web_view_->GetPage()->GetSettings().SetAccessibilityFontScaleFactor(scale);
+}
+
+void DevToolsEmulator::ResetEmulatedAccessibilityFontScaleFactor() {
+  if (accessibility_font_scale_emulation_enabled_) {
+    web_view_->GetPage()->GetSettings().SetAccessibilityFontScaleFactor(
+        embedder_accessibility_font_scale_);
+    accessibility_font_scale_emulation_enabled_ = false;
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/dev_tools_emulator.h b/third_party/blink/renderer/core/inspector/dev_tools_emulator.h
index c365bc0..676047a 100644
--- a/third_party/blink/renderer/core/inspector/dev_tools_emulator.h
+++ b/third_party/blink/renderer/core/inspector/dev_tools_emulator.h
@@ -69,6 +69,10 @@
   void SetAutoDarkModeOverride(bool);
   void ResetAutoDarkModeOverride();
 
+  void SetAccessibilityFontScaleFactor(double scale);
+  void SetEmulatedAccessibilityFontScaleFactor(double scale);
+  void ResetEmulatedAccessibilityFontScaleFactor();
+
   bool HasViewportOverride() const { return !!viewport_override_; }
 
   // Notify the DevToolsEmulator about a scroll or scale change of the
@@ -160,6 +164,9 @@
 
   bool embedder_force_dark_mode_enabled_;
   bool auto_dark_overriden_;
+
+  double embedder_accessibility_font_scale_;
+  bool accessibility_font_scale_emulation_enabled_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
index dd6f535..f237543a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
@@ -95,6 +95,8 @@
       emulated_media_features_(&agent_state_, /*default_value=*/WTF::String()),
       emulated_vision_deficiency_(&agent_state_,
                                   /*default_value=*/WTF::String()),
+      os_text_scale_emulation_enabled_(&agent_state_, /*default_value=*/false),
+      emulated_os_text_scale_(&agent_state_, /*default_value=*/1),
       navigator_platform_override_(&agent_state_,
                                    /*default_value=*/WTF::String()),
       hardware_concurrency_override_(&agent_state_, /*default_value=*/0),
@@ -171,11 +173,17 @@
   setEmulatedMedia(emulated_media_.Get(), std::move(features));
   if (!emulated_vision_deficiency_.Get().IsNull())
     setEmulatedVisionDeficiency(emulated_vision_deficiency_.Get());
+  if (os_text_scale_emulation_enabled_.Get()) {
+    setEmulatedOSTextScale(emulated_os_text_scale_.Get());
+  }
   auto status_or_rgba = protocol::DOM::RGBA::ReadFrom(
       default_background_color_override_rgba_.Get());
   if (status_or_rgba.ok())
     setDefaultBackgroundColorOverride(std::move(status_or_rgba).value());
-  setFocusEmulationEnabled(emulate_focus_.Get());
+  if (emulate_focus_.Get()) {
+    setFocusEmulationEnabled(true);
+  }
+
   if (emulate_auto_dark_mode_.Get())
     setAutoDarkModeOverride(auto_dark_mode_override_.Get());
   if (!timezone_id_override_.Get().IsNull())
@@ -242,8 +250,11 @@
       std::make_unique<protocol::Array<protocol::Emulation::MediaFeature>>());
   if (!emulated_vision_deficiency_.Get().IsNull())
     setEmulatedVisionDeficiency(String("none"));
+  setEmulatedOSTextScale(std::nullopt);
   setCPUThrottlingRate(1);
-  setFocusEmulationEnabled(false);
+  if (emulate_focus_.Get()) {
+    setFocusEmulationEnabled(false);
+  }
   if (emulate_auto_dark_mode_.Get()) {
     setAutoDarkModeOverride(std::nullopt);
   }
@@ -449,6 +460,27 @@
   return response;
 }
 
+protocol::Response InspectorEmulationAgent::setEmulatedOSTextScale(
+    std::optional<double> scale) {
+  protocol::Response response = AssertPage();
+  if (!response.IsSuccess()) {
+    return response;
+  }
+  if (scale.has_value()) {
+    os_text_scale_emulation_enabled_.Set(true);
+    emulated_os_text_scale_.Set(scale.value());
+    GetWebViewImpl()
+        ->GetDevToolsEmulator()
+        ->SetEmulatedAccessibilityFontScaleFactor(scale.value());
+  } else {
+    os_text_scale_emulation_enabled_.Set(false);
+    GetWebViewImpl()
+        ->GetDevToolsEmulator()
+        ->ResetEmulatedAccessibilityFontScaleFactor();
+  }
+  return response;
+}
+
 protocol::Response InspectorEmulationAgent::setCPUThrottlingRate(double rate) {
   protocol::Response response = AssertPage();
   if (!response.IsSuccess())
@@ -463,9 +495,6 @@
   protocol::Response response = AssertPage();
   if (!response.IsSuccess())
     return response;
-  if (enabled == emulate_focus_.Get()) {
-    return response;
-  }
   emulate_focus_.Set(enabled);
   GetWebViewImpl()->GetPage()->GetFocusController().SetFocusEmulationEnabled(
       enabled);
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
index 49bd6e2..346e0378 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
@@ -55,6 +55,7 @@
       std::unique_ptr<protocol::Array<protocol::Emulation::MediaFeature>>
           features) override;
   protocol::Response setEmulatedVisionDeficiency(const String&) override;
+  protocol::Response setEmulatedOSTextScale(std::optional<double>) override;
   protocol::Response setCPUThrottlingRate(double) override;
   protocol::Response setFocusEmulationEnabled(bool) override;
   protocol::Response setAutoDarkModeOverride(std::optional<bool>) override;
@@ -159,6 +160,8 @@
   InspectorAgentState::String emulated_media_;
   InspectorAgentState::StringMap emulated_media_features_;
   InspectorAgentState::String emulated_vision_deficiency_;
+  InspectorAgentState::Boolean os_text_scale_emulation_enabled_;
+  InspectorAgentState::Double emulated_os_text_scale_;
   InspectorAgentState::String navigator_platform_override_;
   InspectorAgentState::Integer hardware_concurrency_override_;
   InspectorAgentState::String user_agent_override_;
diff --git a/third_party/blink/renderer/core/inspector/inspector_protocol_config.json b/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
index b06549c..7b6e9fbb 100644
--- a/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
+++ b/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
@@ -98,7 +98,7 @@
             {
                 "domain": "Emulation",
                 "include": ["forceViewport", "resetViewport", "resetPageScaleFactor", "setPageScaleFactor", "setScriptExecutionDisabled", "setTouchEmulationEnabled", "setSmallViewportHeightDifferenceOverride",
-                            "setEmulatedMedia", "setEmulatedVisionDeficiency", "setCPUThrottlingRate", "setVirtualTimePolicy", "setTimezoneOverride", "setNavigatorOverrides", "setDefaultBackgroundColorOverride", "setSafeAreaInsetsOverride", "setDeviceMetricsOverride", "clearDeviceMetricsOverride",
+                            "setEmulatedMedia", "setEmulatedVisionDeficiency", "setEmulatedOSTextScale", "setCPUThrottlingRate", "setVirtualTimePolicy", "setTimezoneOverride", "setNavigatorOverrides", "setDefaultBackgroundColorOverride", "setSafeAreaInsetsOverride", "setDeviceMetricsOverride", "clearDeviceMetricsOverride",
                             "setHardwareConcurrencyOverride", "setUserAgentOverride", "setScrollbarsHidden", "setDocumentCookieDisabled", "setFocusEmulationEnabled", "setAutoDarkModeOverride", "setLocaleOverride", "setDisabledImageTypes", "setAutomationOverride"],
                 "include_events": ["virtualTimeBudgetExpired", "virtualTimeAdvanced", "virtualTimePaused"]
             },
diff --git a/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
index d5e50ad2..1a80efa8 100644
--- a/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
@@ -343,6 +343,9 @@
 
     // "last" here refers to last in the block direction.
     bool is_last_edge_intersection = flex_line_index == flex_lines.size() - 1;
+    // "last" here refers to last in the inline direction.
+    bool is_last_item_in_line =
+        item_index_in_line == flex_line.item_indices.size() - 1;
 
     if (item_index_in_line == 0) {
       // For the first item in each line, the intersection associated with
@@ -369,26 +372,37 @@
       if (is_first_edge_intersection) {
         PopulateGapIntersectionsForFirstLine(
             flex_line, flex_lines.size(),
-            item_index_in_line == flex_line.item_indices.size() - 1,
             main_intersection_offset, item_cross_intersections_list);
       } else if (is_last_edge_intersection) {
         PopulateGapIntersectionsForLastLine(flex_line, main_intersection_offset,
                                             item_cross_intersections_list);
       } else {
-        PopulateGapIntersectionsForMiddleItem(
-            flex_lines, item_index_in_line == flex_line.item_indices.size() - 1,
-            flex_line_index, main_intersection_offset,
-            item_cross_intersections_list);
+        PopulateGapIntersectionsForMiddleItem(flex_lines, flex_line_index,
+                                              main_intersection_offset,
+                                              item_cross_intersections_list);
       }
 
       cross_axis_gaps_.push_back(std::move(item_cross_intersections_list));
     }
+
+    if (is_last_item_in_line && flex_lines.size() > 1 &&
+        !is_last_edge_intersection) {
+      // If we are the last item in any line except the last, we add the
+      // intersection of the next main gap with the edge of the container to the
+      // main axis gap intersections.
+      LayoutUnit next_main_axis_gap_start = flex_line.LineCrossEnd();
+      LayoutUnit next_main_axis_gap_end =
+          flex_line.LineCrossEnd() + gap_between_lines_;
+      LayoutUnit cross_intersection_offset =
+          (next_main_axis_gap_start + next_main_axis_gap_end) / 2;
+      PopulateNextMainAxisGapIntersectionsForLastItem(
+          cross_intersection_offset);
+    }
   }
 
   void PopulateGapIntersectionsForFirstLine(
       const FlexLine& flex_line,
       wtf_size_t num_lines,
-      bool is_last_item_in_line,
       LayoutUnit main_intersection_offset,
       Vector<GapIntersection>& item_cross_intersections_list) {
     // This method assumes that the inline offset of the
@@ -418,10 +432,6 @@
       AddGapIntersectionToResults(main_intersection_offset,
                                   cross_intersection_offset,
                                   main_intersections_after_current_line_);
-
-      if (is_last_item_in_line) {
-        PopulateMainAxisGapIntersectionsForLastItem(cross_intersection_offset);
-      }
     }
   }
 
@@ -481,7 +491,7 @@
     main_intersections_before_current_line_.clear();
   }
 
-  void PopulateMainAxisGapIntersectionsForLastItem(
+  void PopulateNextMainAxisGapIntersectionsForLastItem(
       LayoutUnit cross_intersection_offset) {
     CHECK(container_builder_);
     // If we are the last item on the line, we add the intersection
@@ -502,7 +512,6 @@
 
   void PopulateGapIntersectionsForMiddleItem(
       const FlexLineVector& flex_lines,
-      bool is_last_item_in_line,
       size_t flex_line_index,
       LayoutUnit main_intersection_offset,
       Vector<GapIntersection>& item_cross_intersections_list) {
@@ -536,10 +545,6 @@
     AddGapIntersectionToResults(main_intersection_offset,
                                 cross_intersection_offset,
                                 item_cross_intersections_list);
-
-    if (is_last_item_in_line) {
-      PopulateMainAxisGapIntersectionsForLastItem(cross_intersection_offset);
-    }
   }
 
   void PopulateGapIntersectionsForLastLine(
diff --git a/third_party/blink/renderer/core/loader/anchor_element_interaction_test.cc b/third_party/blink/renderer/core/loader/anchor_element_interaction_test.cc
index 16e2c15..10153a2 100644
--- a/third_party/blink/renderer/core/loader/anchor_element_interaction_test.cc
+++ b/third_party/blink/renderer/core/loader/anchor_element_interaction_test.cc
@@ -362,7 +362,7 @@
 
   // Wait for hover logic to process the event
   task_runner->AdvanceTimeAndRun(
-      AnchorElementInteractionTracker::GetHoverDwellTime());
+      AnchorElementInteractionTracker::kModerateHoverDwellTime);
   base::RunLoop().RunUntilIdle();
 
   KURL expected_url = KURL("https://anchor1.com/");
@@ -397,7 +397,7 @@
 
   // Wait for hover logic to process the event
   task_runner->AdvanceTimeAndRun(
-      0.5 * AnchorElementInteractionTracker::GetHoverDwellTime());
+      0.5 * AnchorElementInteractionTracker::kModerateHoverDwellTime);
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(1u, hosts_.size());
@@ -430,7 +430,7 @@
       mouse_enter_event, Vector<WebMouseEvent>(), Vector<WebMouseEvent>());
 
   task_runner->AdvanceTimeAndRun(
-      0.5 * AnchorElementInteractionTracker::GetHoverDwellTime());
+      0.5 * AnchorElementInteractionTracker::kModerateHoverDwellTime);
 
   WebMouseEvent mouse_leave_event(
       WebInputEvent::Type::kMouseLeave, coordinates, coordinates,
@@ -441,7 +441,7 @@
 
   // Wait for hover logic to process the event
   task_runner->AdvanceTimeAndRun(
-      AnchorElementInteractionTracker::GetHoverDwellTime());
+      AnchorElementInteractionTracker::kModerateHoverDwellTime);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, hosts_.size());
@@ -586,7 +586,7 @@
   constexpr gfx::Vector2dF velocity{40, -30};
   constexpr base::TimeDelta timestep = base::Milliseconds(20);
   for (base::TimeDelta t;
-       t <= AnchorElementInteractionTracker::GetHoverDwellTime();
+       t <= AnchorElementInteractionTracker::kModerateHoverDwellTime;
        t += timestep) {
     gfx::PointF coordinates =
         origin + gfx::ScaleVector2d(velocity, t.InSecondsF());
diff --git a/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc b/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc
index 8442356dc..f54cae8 100644
--- a/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc
+++ b/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.cc
@@ -291,14 +291,6 @@
   AnchorElementViewportPositionTracker::Observer::Trace(visitor);
 }
 
-// static
-base::TimeDelta AnchorElementInteractionTracker::GetHoverDwellTime() {
-  static base::FeatureParam<base::TimeDelta> hover_dwell_time{
-      &blink::features::kSpeculationRulesPointerHoverHeuristics,
-      "HoverDwellTime", base::Milliseconds(200)};
-  return hover_dwell_time.Get();
-}
-
 void AnchorElementInteractionTracker::OnMouseMoveEvent(
     const WebMouseEvent& mouse_event) {
   mouse_motion_estimator_->OnMouseMoveEvent(mouse_event.PositionInScreen());
@@ -367,9 +359,9 @@
                  .is_mouse =
                      pointer_event.pointerType() == pointer_type_names::kMouse,
                  .anchor_id = AnchorElementId(*anchor),
-                 .timestamp = clock_->NowTicks() + GetHoverDwellTime()});
+                 .timestamp = clock_->NowTicks() + kModerateHoverDwellTime});
     if (!hover_timer_.IsActive()) {
-      hover_timer_.StartOneShot(GetHoverDwellTime(), FROM_HERE);
+      hover_timer_.StartOneShot(kModerateHoverDwellTime, FROM_HERE);
     }
   } else if (event_type == event_type_names::kPointerout) {
     // Since the pointer is no longer hovering on the link, there is no need to
diff --git a/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.h b/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.h
index 180ddf3..44db3e20 100644
--- a/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.h
+++ b/third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.h
@@ -83,7 +83,8 @@
   explicit AnchorElementInteractionTracker(Document& document);
   virtual ~AnchorElementInteractionTracker();
 
-  static base::TimeDelta GetHoverDwellTime();
+  static constexpr base::TimeDelta kModerateHoverDwellTime{
+      base::Milliseconds(200)};
 
   void OnMouseMoveEvent(const WebMouseEvent& mouse_event);
   void OnPointerEvent(EventTarget& target, const PointerEvent& pointer_event);
diff --git a/third_party/blink/renderer/core/page/focus_controller.cc b/third_party/blink/renderer/core/page/focus_controller.cc
index 455a851..cab94f3 100644
--- a/third_party/blink/renderer/core/page/focus_controller.cc
+++ b/third_party/blink/renderer/core/page/focus_controller.cc
@@ -1325,8 +1325,17 @@
 
 void FocusController::SetFocusedFrame(Frame* frame, bool notify_embedder) {
   DCHECK(!frame || frame->GetPage() == page_);
-  if (focused_frame_ == frame || (is_changing_focused_frame_ && frame))
+  if (focused_frame_ == frame || (is_changing_focused_frame_ && frame)) {
     return;
+  }
+
+  // DevTools starts emulating focus early in the lifecycle.
+  // blink calls SetFocusedFrame(nullptr) after DevTools has already
+  // set a focused frame. Returning early to not discard previously
+  // set emulation state.
+  if (is_emulating_focus_ && !frame) {
+    return;
+  }
 
   is_changing_focused_frame_ = true;
 
@@ -1453,6 +1462,12 @@
     return false;
   }
 
+  // If DevTools is emulating focus, any document
+  // is focused irrespective of the tree.
+  if (is_emulating_focus_) {
+    return true;
+  }
+
   if (IsA<HTMLFrameOwnerElement>(focused_frame_->Owner())) {
     auto* fenced_frame = DynamicTo<HTMLFencedFrameElement>(
         To<HTMLFrameOwnerElement>(focused_frame_->Owner()));
diff --git a/third_party/blink/renderer/modules/ai/availability.cc b/third_party/blink/renderer/modules/ai/availability.cc
index 46b7632..6d63421 100644
--- a/third_party/blink/renderer/modules/ai/availability.cc
+++ b/third_party/blink/renderer/modules/ai/availability.cc
@@ -89,6 +89,9 @@
           execution_context, AIMetrics::AISessionType::kTranslator,
           mojom::blink::ModelAvailabilityCheckResult::
               kUnavailableUnsupportedLanguage);
+    case mojom::blink::CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
+    case mojom::blink::CanCreateTranslatorResult::
+        kNoExceedsLanguagePackCountLimitation:
     case mojom::blink::CanCreateTranslatorResult::kNoServiceCrashed:
     case mojom::blink::CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case mojom::blink::CanCreateTranslatorResult::
diff --git a/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc b/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc
index ad43f45b..3b36977 100644
--- a/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc
+++ b/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc
@@ -21,6 +21,10 @@
 
 const char kExceptionMessageUnableToCreateTranslator[] =
     "Unable to create translator for the given source and target language.";
+const char kLinkToDocument[] =
+    "See "
+    "https://developer.chrome.com/docs/ai/translator-api?#supported-languages "
+    "for more details.";
 
 String ConvertCreateTranslatorErrorToDebugString(CreateTranslatorError error) {
   switch (error) {
@@ -32,6 +36,14 @@
       return "Failed to initialize the translation library.";
     case CreateTranslatorError::kFailedToCreateTranslator:
       return "The translation library failed to create a translator.";
+    case CreateTranslatorError::kAcceptLanguagesCheckFailed:
+      return String(base::StrCat(
+          {"The preferred languages check for Translator API failed. ",
+           kLinkToDocument}));
+    case CreateTranslatorError::kExceedsLanguagePackCountLimitation:
+      return String(base::StrCat(
+          {"The Translator API language pack count exceeded the limitation. ",
+           kLinkToDocument}));
     case CreateTranslatorError::kServiceCrashed:
       return "The translation service crashed.";
     case CreateTranslatorError::kDisallowedByPolicy:
@@ -58,6 +70,13 @@
       NOTREACHED();
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
       return "The language pair is unsupported.";
+    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
+      equivalent_error = CreateTranslatorError::kAcceptLanguagesCheckFailed;
+      break;
+    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
+      equivalent_error =
+          CreateTranslatorError::kExceedsLanguagePackCountLimitation;
+      break;
     case CanCreateTranslatorResult::kNoServiceCrashed:
       equivalent_error = CreateTranslatorError::kServiceCrashed;
       break;
@@ -82,6 +101,8 @@
       return true;
     case CanCreateTranslatorResult::kReadily:
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
+    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
+    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
     case CanCreateTranslatorResult::kNoServiceCrashed:
     case CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case CanCreateTranslatorResult::kNoExceedsServiceCountLimitation:
@@ -99,6 +120,8 @@
     case CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired:
       return false;
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
+    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
+    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
     case CanCreateTranslatorResult::kNoServiceCrashed:
     case CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case CanCreateTranslatorResult::kNoExceedsServiceCountLimitation:
@@ -230,7 +253,8 @@
   // they lack the ability to do so.
   CHECK(window != nullptr || context->IsServiceWorkerGlobalScope());
 
-  if (!context->IsServiceWorkerGlobalScope() &&
+  if (RuntimeEnabledFeatures::TranslationAPIV1Enabled() &&
+      !context->IsServiceWorkerGlobalScope() &&
       RequiresUserActivation(result) &&
       !LocalFrame::ConsumeTransientUserActivation(window->GetFrame())) {
     GetResolver()->RejectWithDOMException(
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
index e921050..3665d97 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
@@ -16,6 +16,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "cc/paint/skia_paint_canvas.h"
diff --git a/third_party/blink/renderer/modules/mediastream/DEPS b/third_party/blink/renderer/modules/mediastream/DEPS
index 5b02fbb..1c7ea94 100644
--- a/third_party/blink/renderer/modules/mediastream/DEPS
+++ b/third_party/blink/renderer/modules/mediastream/DEPS
@@ -4,6 +4,7 @@
     "+base/strings/utf_string_conversions.h",
     "+base/strings/stringprintf.h",
     "+base/containers/flat_map.h",
+    "+base/memory/singleton.h",
 
     # TODO(crbug.com/923394): Remove these dependencies once per-frame
     # task runners are used in all cases.
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc b/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
index 581f153..0145ec2 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
@@ -14,6 +14,7 @@
 #include "base/functional/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/singleton.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
diff --git a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
index 44919cd..6a255188 100644
--- a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
+++ b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
@@ -76,7 +76,8 @@
   UTF16RagelIterator operator-(int v) { return *this + -v; }
 
   int operator-(const UTF16RagelIterator& other) {
-    DCHECK_EQ(buffer_, other.buffer_);
+    DCHECK_EQ(buffer_.data(), other.buffer_.data());
+    DCHECK_EQ(buffer_.size(), other.buffer_.size());
     return cursor_ - other.cursor_;
   }
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_hibernation_handler_test.cc b/third_party/blink/renderer/platform/graphics/canvas_hibernation_handler_test.cc
index 976b28c7..2d0fc34 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_hibernation_handler_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_hibernation_handler_test.cc
@@ -9,6 +9,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/trace_event/process_memory_dump.h"
 #include "components/viz/test/test_context_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
diff --git a/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc b/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc
index fb19ca6d..1977fed4 100644
--- a/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_util.h"
+#include "base/strings/to_string.h"
 #include "base/strings/utf_string_conversions.h"
 #include "media/base/key_systems.h"
 #include "media/base/media_permission.h"
@@ -61,8 +62,6 @@
     case media::CreateCdmStatus::kNotAllowedOnUniqueOrigin:
       return "EME use is not allowed on unique origins.";
 #if BUILDFLAG(IS_ANDROID)
-    case media::CreateCdmStatus::kMediaDrmBridgeCreationFailed:
-      return "MediaDrmBridge creation failed.";
     case media::CreateCdmStatus::kMediaCryptoNotAvailable:
       return "MediaCrypto not available.";
     case media::CreateCdmStatus::kAndroidMediaDrmIllegalArgument:
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
index 778ce23..653e44a 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
@@ -1971,7 +1971,9 @@
 void WebMediaPlayerImpl::RestartForHls() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 #if BUILDFLAG(ENABLE_HLS_DEMUXER)
-  observer_->OnHlsManifestDetected();
+  if (observer_) {
+    observer_->OnHlsManifestDetected();
+  }
   SetMemoryReportingState(false);
   StartPipeline();
 #else
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc
index f0e0b37..37702c5 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc
@@ -24,7 +24,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "media/base/media_log.h"
 #include "media/base/media_switches.h"
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc
index 03e004f..b4faf18 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc
@@ -12,7 +12,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/task/sequenced_task_runner.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "media/base/media_util.h"
 #include "media/base/platform_features.h"
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index e246ce8..85d0b2e 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -4648,14 +4648,6 @@
       status: "stable",
     },
     {
-      name: "SpeculationRulesPointerDownHeuristics",
-      base_feature_status: "enabled",
-    },
-    {
-      name: "SpeculationRulesPointerHoverHeuristics",
-      base_feature_status: "enabled",
-    },
-    {
       name: "SpeculationRulesPrefetchWithSubresources",
     },
     {
@@ -5045,12 +5037,23 @@
       origin_trial_allows_third_party: true,
       base_feature_status: "enabled",
       copied_from_base_feature_if: "overridden",
+      implied_by: ["TranslationAPIV1"],
     },
     {
       name: "TranslationAPIForWorkers",
       public: true,
     },
     {
+      name: "TranslationAPIV1",
+      status: {
+        "Win": "experimental",
+        "Mac": "experimental",
+        "Linux": "experimental",
+        "default": "",
+      },
+      copied_from_base_feature_if: "overridden",
+    },
+    {
       // Ensures symbols are treated as word boundaries during traversal.
       // See https://crbug.com/40252642
       name: "TreatSymbolsAsWordBoundary",
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
index fd4da58..dc1c7fa 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
@@ -11,7 +11,7 @@
 #include "base/functional/bind.h"
 #include "base/task/common/scoped_defer_task_posting.h"
 #include "base/task/single_thread_task_runner.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/platform/scheduler/common/blink_scheduler_single_thread_task_runner.h"
 #include "third_party/blink/renderer/platform/scheduler/common/tracing_helper.h"
diff --git a/third_party/blink/renderer/platform/text/layout_locale.cc b/third_party/blink/renderer/platform/text/layout_locale.cc
index c37b4f3c..f9a5c69 100644
--- a/third_party/blink/renderer/platform/text/layout_locale.cc
+++ b/third_party/blink/renderer/platform/text/layout_locale.cc
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/platform/text/icu_error.h"
 #include "third_party/blink/renderer/platform/text/locale_to_script_mapping.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 #include "third_party/blink/renderer/platform/wtf/text/case_folding_hash.h"
 #include "third_party/blink/renderer/platform/wtf/thread_specific.h"
@@ -26,6 +27,32 @@
 
 namespace {
 
+using CaseFoldingHashSet = HashSet<String, CaseFoldingHashTraits<String>>;
+
+CaseFoldingHashSet CreateMacrolanguageChineseLanguageTags() {
+  // This list is from the IANA language-subtag-registry:
+  // https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
+  // where "Type: language" and "Macrolanguage: zh".
+  return CaseFoldingHashSet{"cdo", "cjy", "cmn", "cnp", "cpx", "csp", "czh",
+                            "czo", "gan", "hak", "hnm", "hsn", "luh", "lzh",
+                            "mnp", "nan", "sjc", "wuu", "yue", "zh"};
+}
+
+CaseFoldingHashSet MacrolanguageChineseLanguageTags() {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(CaseFoldingHashSet, tags,
+                                  (CreateMacrolanguageChineseLanguageTags()));
+  return tags;
+}
+
+bool ComputeIsMacrolanguageChinese(const String& value) {
+  const wtf_size_t separater = value.find('-');
+  if (separater == kNotFound) {
+    return MacrolanguageChineseLanguageTags().Contains(value);
+  }
+  const StringView language{value, 0, separater};
+  return MacrolanguageChineseLanguageTags().Contains(language.ToString());
+}
+
 struct PerThreadData {
   HashMap<AtomicString,
           scoped_refptr<LayoutLocale>,
@@ -210,6 +237,12 @@
   return locale;
 }
 
+bool LayoutLocale::IsMacrolanguageChineseSlow() const {
+  is_macrolanguage_chinese_computed_ = true;
+  is_macrolanguage_chinese_ = ComputeIsMacrolanguageChinese(string_);
+  return is_macrolanguage_chinese_;
+}
+
 void LayoutLocale::ComputeCaseMapLocale() const {
   DCHECK(!case_map_computed_);
   case_map_computed_ = true;
@@ -219,12 +252,7 @@
 LayoutLocale::LayoutLocale(const AtomicString& locale)
     : string_(locale),
       harfbuzz_language_(ToHarfbuzLanguage(locale)),
-      script_(LocaleToScriptCodeForFontSelection(locale)),
-      script_for_han_(USCRIPT_COMMON),
-      has_script_for_han_(false),
-      hyphenation_computed_(false),
-      quotes_data_computed_(false),
-      case_map_computed_(false) {}
+      script_(LocaleToScriptCodeForFontSelection(locale)) {}
 
 // static
 const LayoutLocale* LayoutLocale::Get(const AtomicString& locale) {
diff --git a/third_party/blink/renderer/platform/text/layout_locale.h b/third_party/blink/renderer/platform/text/layout_locale.h
index 4fef38f..dc0d143 100644
--- a/third_party/blink/renderer/platform/text/layout_locale.h
+++ b/third_party/blink/renderer/platform/text/layout_locale.h
@@ -64,6 +64,11 @@
   static const LayoutLocale* LocaleForHan(const LayoutLocale*);
   const char* LocaleForHanForSkFontMgr() const;
 
+  bool IsMacrolanguageChinese() const {
+    return is_macrolanguage_chinese_computed_ ? is_macrolanguage_chinese_
+                                              : IsMacrolanguageChineseSlow();
+  }
+
   // The normalized locale data to construct |CaseMap| from.
   const CaseMap::Locale& CaseMapLocale() const {
     if (case_map_computed_)
@@ -89,6 +94,8 @@
  private:
   explicit LayoutLocale(const AtomicString&);
 
+  bool IsMacrolanguageChineseSlow() const;
+
   void ComputeScriptForHan() const;
   void ComputeCaseMapLocale() const;
 
@@ -102,12 +109,14 @@
   raw_ptr<const hb_language_impl_t> harfbuzz_language_;
 
   UScriptCode script_;
-  mutable UScriptCode script_for_han_;
+  mutable UScriptCode script_for_han_ = USCRIPT_COMMON;
 
-  mutable unsigned has_script_for_han_ : 1;
-  mutable unsigned hyphenation_computed_ : 1;
-  mutable unsigned quotes_data_computed_ : 1;
-  mutable unsigned case_map_computed_ : 1;
+  mutable unsigned has_script_for_han_ : 1 = false;
+  mutable unsigned hyphenation_computed_ : 1 = false;
+  mutable unsigned quotes_data_computed_ : 1 = false;
+  mutable unsigned case_map_computed_ : 1 = false;
+  mutable unsigned is_macrolanguage_chinese_computed_ : 1 = false;
+  mutable unsigned is_macrolanguage_chinese_ : 1 = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/text/layout_locale_test.cc b/third_party/blink/renderer/platform/text/layout_locale_test.cc
index 119877c2..be9c8d718 100644
--- a/third_party/blink/renderer/platform/text/layout_locale_test.cc
+++ b/third_party/blink/renderer/platform/text/layout_locale_test.cc
@@ -39,6 +39,7 @@
   UScriptCode script;
   const char* sk_font_mgr = nullptr;
   std::optional<UScriptCode> script_for_han;
+  bool is_macrolanguage_chinese = false;
 } locale_test_data[] = {
     // Country is not relevant to |SkFontMgr|.
     {"en-US", USCRIPT_LATIN, "en"},
@@ -65,31 +66,31 @@
   USCRIPT_TRADITIONAL_HAN, "zh-Hant", USCRIPT_TRADITIONAL_HAN
     {"ja-JP", EXPECT_JAPANESE},
     {"ko-KR", EXPECT_KOREAN},
-    {"zh", EXPECT_SIMPLIFIED_CHINESE},
-    {"zh-CN", EXPECT_SIMPLIFIED_CHINESE},
-    {"zh-HK", EXPECT_TRADITIONAL_CHINESE},
-    {"zh-MO", EXPECT_TRADITIONAL_CHINESE},
-    {"zh-SG", EXPECT_SIMPLIFIED_CHINESE},
-    {"zh-TW", EXPECT_TRADITIONAL_CHINESE},
+    {"zh", EXPECT_SIMPLIFIED_CHINESE, true},
+    {"zh-CN", EXPECT_SIMPLIFIED_CHINESE, true},
+    {"zh-HK", EXPECT_TRADITIONAL_CHINESE, true},
+    {"zh-MO", EXPECT_TRADITIONAL_CHINESE, true},
+    {"zh-SG", EXPECT_SIMPLIFIED_CHINESE, true},
+    {"zh-TW", EXPECT_TRADITIONAL_CHINESE, true},
 
     // Encompassed languages within the Chinese macrolanguage.
     // Both "lang" and "lang-extlang" should work.
-    {"nan", EXPECT_TRADITIONAL_CHINESE},
-    {"wuu", EXPECT_SIMPLIFIED_CHINESE},
-    {"yue", EXPECT_TRADITIONAL_CHINESE},
-    {"zh-nan", EXPECT_TRADITIONAL_CHINESE},
-    {"zh-wuu", EXPECT_SIMPLIFIED_CHINESE},
-    {"zh-yue", EXPECT_TRADITIONAL_CHINESE},
+    {"nan", EXPECT_TRADITIONAL_CHINESE, true},
+    {"wuu", EXPECT_SIMPLIFIED_CHINESE, true},
+    {"yue", EXPECT_TRADITIONAL_CHINESE, true},
+    {"zh-nan", EXPECT_TRADITIONAL_CHINESE, true},
+    {"zh-wuu", EXPECT_SIMPLIFIED_CHINESE, true},
+    {"zh-yue", EXPECT_TRADITIONAL_CHINESE, true},
 
     // Specified scripts is honored.
-    {"zh-Hans", EXPECT_SIMPLIFIED_CHINESE},
-    {"zh-Hant", EXPECT_TRADITIONAL_CHINESE},
+    {"zh-Hans", EXPECT_SIMPLIFIED_CHINESE, true},
+    {"zh-Hant", EXPECT_TRADITIONAL_CHINESE, true},
 
     // Lowercase scripts should be capitalized.
     // |SkFontMgr_Android| uses case-sensitive match, and `fonts.xml` has
     // capitalized script names.
-    {"zh-hans", EXPECT_SIMPLIFIED_CHINESE},
-    {"zh-hant", EXPECT_TRADITIONAL_CHINESE},
+    {"zh-hans", EXPECT_SIMPLIFIED_CHINESE, true},
+    {"zh-hant", EXPECT_TRADITIONAL_CHINESE, true},
 
     // Script has priority over other subtags.
     {"en-Hans", EXPECT_SIMPLIFIED_CHINESE},
@@ -98,10 +99,10 @@
     {"en-Hant-CN", EXPECT_TRADITIONAL_CHINESE},
     {"en-TW-Hans", EXPECT_SIMPLIFIED_CHINESE},
     {"en-CN-Hant", EXPECT_TRADITIONAL_CHINESE},
-    {"wuu-Hant", EXPECT_TRADITIONAL_CHINESE},
-    {"yue-Hans", EXPECT_SIMPLIFIED_CHINESE},
-    {"zh-wuu-Hant", EXPECT_TRADITIONAL_CHINESE},
-    {"zh-yue-Hans", EXPECT_SIMPLIFIED_CHINESE},
+    {"wuu-Hant", EXPECT_TRADITIONAL_CHINESE, true},
+    {"yue-Hans", EXPECT_SIMPLIFIED_CHINESE, true},
+    {"zh-wuu-Hant", EXPECT_TRADITIONAL_CHINESE, true},
+    {"zh-yue-Hans", EXPECT_SIMPLIFIED_CHINESE, true},
 
     // Lang has priority over region.
     // icu::Locale::getDefault() returns other combinations if, for instance,
@@ -110,10 +111,10 @@
     {"ja-US", EXPECT_JAPANESE},
     {"ko", EXPECT_KOREAN},
     {"ko-US", EXPECT_KOREAN},
-    {"wuu-TW", EXPECT_SIMPLIFIED_CHINESE},
-    {"yue-CN", EXPECT_TRADITIONAL_CHINESE},
-    {"zh-wuu-TW", EXPECT_SIMPLIFIED_CHINESE},
-    {"zh-yue-CN", EXPECT_TRADITIONAL_CHINESE},
+    {"wuu-TW", EXPECT_SIMPLIFIED_CHINESE, true},
+    {"yue-CN", EXPECT_TRADITIONAL_CHINESE, true},
+    {"zh-wuu-TW", EXPECT_SIMPLIFIED_CHINESE, true},
+    {"zh-yue-CN", EXPECT_TRADITIONAL_CHINESE, true},
 
     // Region should not affect script, but it can influence scriptForHan.
     {"en-CN", USCRIPT_LATIN, "en"},
@@ -154,6 +155,7 @@
   } else {
     EXPECT_EQ(USCRIPT_SIMPLIFIED_HAN, locale->GetScriptForHan()) << test.locale;
   }
+  EXPECT_EQ(test.is_macrolanguage_chinese, locale->IsMacrolanguageChinese());
   if (test.sk_font_mgr)
     EXPECT_STREQ(test.sk_font_mgr, locale->LocaleForSkFontMgr()) << test.locale;
 }
diff --git a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
index ba28c0c..4f346ea 100644
--- a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
+++ b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
@@ -106,11 +106,6 @@
   PartitionOptions opts;
   opts.backup_ref_ptr = brp_setting;
   opts.memory_tagging = {.enabled = memory_tagging};
-  opts.use_small_single_slot_spans =
-      base::FeatureList::IsEnabled(
-          base::features::kPartitionAllocUseSmallSingleSlotSpans)
-          ? partition_alloc::PartitionOptions::kEnabled
-          : partition_alloc::PartitionOptions::kDisabled;
   return opts;
 }
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 6b53476..51a4b1d7 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5217,9 +5217,6 @@
 
 crbug.com/365615740 fast/events/wheel/mouse-wheel-scroll-latching.html [ Failure Pass Timeout ]
 
-# Sheriff 2020-02-28
-crbug.com/40675392 http/tests/devtools/network/network-initiator-chain.js [ Failure Pass ]
-
 crbug.com/1057822 http/tests/misc/synthetic-gesture-initiated-in-cross-origin-frame.html [ Crash Failure Pass ]
 
 # Ecosystem-Infra Sheriff 2020-04-15
@@ -9158,3 +9155,7 @@
 
 # Gardener 2025-06-03
 crbug.com/422174062 [ Win ] external/wpt/css/css-masking/clip-path/clip-path-inline-009.html [ Failure Pass ]
+
+# Gardener 2025-06-06
+crbug.com/415666471 [ Linux ] external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html [ Failure Timeout Pass ]
+crbug.com/415666471 [ Linux ] virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html [ Failure Timeout Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index d8c232fa..fd945466 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1866,7 +1866,7 @@
       "http/tests/inspector-protocol/service-worker/tentative/static-router"
     ],
     "args": ["--enable-features=ServiceWorkerStaticRouterTimingInfo"],
-    "expires": "Jun 5, 2025",
+    "expires": "Dec 5, 2025",
     "owners": ["suzukikeita@chromium.org", "yyanagisawa@chromium.org", "sisidovski@chromium.org"]
   },
   {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/flex/flex-gap-decorations-025-ref.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/flex/flex-gap-decorations-025-ref.html
new file mode 100644
index 0000000..d4cbf50
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/flex/flex-gap-decorations-025-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
+<style>
+    body {
+      margin: 0;
+    }
+
+    main {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 10px;
+    }
+
+    .item {
+      background-color: teal;
+      height: 50px;
+      flex-basis: 100%;
+    }
+
+    .row-gap {
+        position: absolute;
+        top: 50px;
+        left: 0px;
+        background: gold;
+        width: 100%;
+        height: 10px;
+    }
+</style>
+<main>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="row-gap"></div>
+</main>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/flex/flex-gap-decorations-025.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/flex/flex-gap-decorations-025.html
new file mode 100644
index 0000000..42b46a3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/flex/flex-gap-decorations-025.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>
+    CSS Gap Decorations: flex gaps are painted when there's only one flex line
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="match" href="flex-gap-decorations-025-ref.html">
+<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
+<style>
+    body {
+      margin: 0;
+    }
+
+    main {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 10px;
+      row-rule: 10px solid gold;
+    }
+
+    .item {
+      background-color: teal;
+      height: 50px;
+      flex-basis: 100%;
+    }
+</style>
+<main>
+    <div class="item"></div>
+    <div class="item"></div>
+</main>
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/equal.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/equal.https.any.js
index dc01aa1..a974ec0 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/equal.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/equal.https.any.js
@@ -987,7 +987,8 @@
 if (navigator.ml) {
   equalTests.forEach((test) => {
     webnn_conformance_test(
-        buildAndExecuteGraph, getEqualPrecisionTolerance, test);
+        buildAndExecuteGraph, getEqualPrecisionTolerance, test,
+        /*cast_to_supported_type=*/true);
   });
 } else {
   test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/greater.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/greater.https.any.js
index 704e0c45..21e8b07 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/greater.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/greater.https.any.js
@@ -991,7 +991,8 @@
 if (navigator.ml) {
   greaterTests.forEach((test) => {
     webnn_conformance_test(
-        buildAndExecuteGraph, getGreaterPrecisionTolerance, test);
+        buildAndExecuteGraph, getGreaterPrecisionTolerance, test,
+        /*cast_to_supported_type=*/true);
   });
 } else {
   test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/greater_or_equal.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/greater_or_equal.https.any.js
index 28a2e89..f9ab2d6 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/greater_or_equal.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/greater_or_equal.https.any.js
@@ -986,7 +986,8 @@
 if (navigator.ml) {
   greaterOrEqualTests.forEach((test) => {
     webnn_conformance_test(
-        buildAndExecuteGraph, getGreaterOrEqualPrecisionTolerance, test);
+        buildAndExecuteGraph, getGreaterOrEqualPrecisionTolerance, test,
+        /*cast_to_supported_type=*/true);
   });
 } else {
   test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/lesser.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/lesser.https.any.js
index 0588f3bc..8978435 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/lesser.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/lesser.https.any.js
@@ -998,7 +998,8 @@
 if (navigator.ml) {
   lesserTests.forEach((test) => {
     webnn_conformance_test(
-        buildAndExecuteGraph, getLesserPrecisionTolerance, test);
+        buildAndExecuteGraph, getLesserPrecisionTolerance, test,
+        /*cast_to_supported_type=*/true);
   });
 } else {
   test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any.js
index cfcc740..16aa588 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any.js
@@ -1105,7 +1105,8 @@
 if (navigator.ml) {
   lesserOrEqualTests.forEach((test) => {
     webnn_conformance_test(
-        buildAndExecuteGraph, getLesserOrEqualPrecisionTolerance, test);
+        buildAndExecuteGraph, getLesserOrEqualPrecisionTolerance, test,
+        /*cast_to_supported_type=*/true);
   });
 } else {
   test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_and.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_and.https.any.js
index a4d7165..1a03ef54 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_and.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_and.https.any.js
@@ -414,7 +414,9 @@
 
 if (navigator.ml) {
   logicalAndTests.forEach((test) => {
-    webnn_conformance_test(buildAndExecuteGraph, getPrecisionTolerance, test);
+    webnn_conformance_test(
+        buildAndExecuteGraph, getPrecisionTolerance, test,
+        /*cast_to_supported_type=*/true);
   });
 } else {
   test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_not.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_not.https.any.js
index 9d1861d..f8949672 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_not.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_not.https.any.js
@@ -214,7 +214,8 @@
 if (navigator.ml) {
   logicalNotTests.forEach((test) => {
     webnn_conformance_test(
-        buildAndExecuteGraph, getLogicalNotPrecisionTolerance, test);
+        buildAndExecuteGraph, getLogicalNotPrecisionTolerance, test,
+        /*cast_to_supported_type=*/true);
   });
 } else {
   test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_or.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_or.https.any.js
index f8941b6..83c2619 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_or.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_or.https.any.js
@@ -414,7 +414,9 @@
 
 if (navigator.ml) {
   logicalOrTests.forEach((test) => {
-    webnn_conformance_test(buildAndExecuteGraph, getPrecisionTolerance, test);
+    webnn_conformance_test(
+        buildAndExecuteGraph, getPrecisionTolerance, test,
+        /*cast_to_supported_type=*/true);
   });
 } else {
   test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_xor.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_xor.https.any.js
index 533d52aa..7a9446ea 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_xor.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/logical_xor.https.any.js
@@ -414,7 +414,9 @@
 
 if (navigator.ml) {
   logicalXorTests.forEach((test) => {
-    webnn_conformance_test(buildAndExecuteGraph, getPrecisionTolerance, test);
+    webnn_conformance_test(
+        buildAndExecuteGraph, getPrecisionTolerance, test,
+        /*cast_to_supported_type=*/true);
   });
 } else {
 test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/network-initiator-chain-expected.txt b/third_party/blink/web_tests/http/tests/devtools/network/network-initiator-chain-expected.txt
deleted file mode 100644
index 3478cf5..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/network/network-initiator-chain-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-Test that computing the initiator graph works for service worker request.
-
-
-http://127.0.0.1:8000/devtools/service-workers/resources/network-fetch-worker.js
-Initiators http://127.0.0.1:8000/devtools/service-workers/resources/network-fetch-worker.js
-Initiated http://127.0.0.1:8000/devtools/service-workers/resources/network-fetch-worker.js
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/network-initiator-chain.js b/third_party/blink/web_tests/http/tests/devtools/network/network-initiator-chain.js
deleted file mode 100644
index 1e0a5c20..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/network/network-initiator-chain.js
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {TestRunner} from 'test_runner';
-import {NetworkTestRunner} from 'network_test_runner';
-import {ApplicationTestRunner} from 'application_test_runner';
-
-(async function() {
-  TestRunner.addResult(`Test that computing the initiator graph works for service worker request.\n`);
-  await TestRunner.showPanel('network');
-
-  NetworkTestRunner.recordNetwork();
-  let scope = 'http://127.0.0.1:8000/devtools/service-workers/resources/';
-  await ApplicationTestRunner.registerServiceWorker('../service-workers/resources/network-fetch-worker.js', scope);
-
-  var requests = NetworkTestRunner.networkRequests();
-  requests.forEach((request) => {
-    TestRunner.addResult('\n' + request.url());
-    var graph =
-        NetworkTestRunner.networkLog().initiatorGraphForRequest(request);
-    TestRunner.addResult('Initiators ' + Array.from(graph.initiators).map(request => request._url));
-    TestRunner.addResult('Initiated ' + Array.from(graph.initiated.keys()).map(request => request._url));
-  });
-
-  TestRunner.completeTest();
-})();
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/set-focus-emulation-enabled-persists-navigations-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/set-focus-emulation-enabled-persists-navigations-expected.txt
new file mode 100644
index 0000000..bc96091
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/set-focus-emulation-enabled-persists-navigations-expected.txt
@@ -0,0 +1,4 @@
+Tests that page focus emulation persists between navigations
+hasFocus(before navigation):true
+hasFocus(after navigation):true
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/set-focus-emulation-enabled-persists-navigations.js b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/set-focus-emulation-enabled-persists-navigations.js
new file mode 100644
index 0000000..8b36700
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/set-focus-emulation-enabled-persists-navigations.js
@@ -0,0 +1,18 @@
+(async function(/** @type {import('test_runner').TestRunner} */ testRunner) {
+  const { session } = await testRunner
+    .startBlank('Tests that page focus emulation persists between navigations');
+
+  async function logFocus(session, label) {
+    testRunner.log(`hasFocus(${label}):` + await session.evaluate('document.hasFocus()'));
+  }
+
+  await session.protocol.Emulation.setFocusEmulationEnabled({enabled: true});
+
+  await logFocus(session, 'before navigation');
+
+  await session.navigate('/resources/blank.html');
+
+  await logFocus(session, 'after navigation');
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/inspector-protocol/emulation/set-emulated-os-text-scale-expected.txt b/third_party/blink/web_tests/inspector-protocol/emulation/set-emulated-os-text-scale-expected.txt
new file mode 100644
index 0000000..5b601fe
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/emulation/set-emulated-os-text-scale-expected.txt
@@ -0,0 +1,7 @@
+Tests that the OS text scale can be emulated.
+Initial preferred-text-scale: 1.
+After setting scale to 2, preferred-text-scale is 2.
+After setting scale to 2 twice, preferred-text-scale is 2.
+After setting scale to 0.85, preferred-text-scale is 0.85.
+After stopping emulation, preferred-text-scale is 1.
+
diff --git a/third_party/blink/web_tests/inspector-protocol/emulation/set-emulated-os-text-scale.js b/third_party/blink/web_tests/inspector-protocol/emulation/set-emulated-os-text-scale.js
new file mode 100644
index 0000000..705dcc2
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/emulation/set-emulated-os-text-scale.js
@@ -0,0 +1,36 @@
+(async function(/** @type {import('test_runner').TestRunner} */ testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div style='font-size: calc(100px * env(preferred-text-scale, 3));'>a</div>
+    <script>
+      function getPreferredTextScale() {
+        let div = document.querySelector('div');
+        return parseInt(getComputedStyle(div).fontSize) / 100;
+      }
+    </script>
+  `, 'Tests that the OS text scale can be emulated.');
+
+  let initial = await session.evaluate("getPreferredTextScale()");
+  testRunner.log(`Initial preferred-text-scale: ${initial}.`);
+
+  // Test emulating a scale of 2.
+  await dp.Emulation.setEmulatedOSTextScale({scale: 2});
+  let set_2 = await session.evaluate("getPreferredTextScale()");
+  testRunner.log(`After setting scale to 2, preferred-text-scale is ${set_2}.`);
+
+  // Test emulating a scale of 2 when already emulating a scale of 2.
+  await dp.Emulation.setEmulatedOSTextScale({scale: 2});
+  let set_2_again = await session.evaluate("getPreferredTextScale()");
+  testRunner.log(`After setting scale to 2 twice, preferred-text-scale is ${set_2_again}.`);
+
+  // Test emulating a scale of 0.85.
+  await dp.Emulation.setEmulatedOSTextScale({scale: 0.85});
+  let set_085 = await session.evaluate("getPreferredTextScale()");
+  testRunner.log(`After setting scale to 0.85, preferred-text-scale is ${set_085}.`);
+
+  // Test stopping emulation with no scale.
+  await dp.Emulation.setEmulatedOSTextScale({});
+  let set_none = await session.evaluate("getPreferredTextScale()");
+  testRunner.log(`After stopping emulation, preferred-text-scale is ${set_none}.`);
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/equal.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/equal.https.any_cpu-expected.txt
index 1395781..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/equal.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/equal.https.any_cpu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] equal float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/greater.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/greater.https.any_cpu-expected.txt
index 9acf726..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/greater.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/greater.https.any_cpu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] greater float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_cpu-expected.txt
index ab7740d..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_cpu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] greaterOrEqual float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lesser.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lesser.https.any_cpu-expected.txt
index d3d56a94..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lesser.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lesser.https.any_cpu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] lesser float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_cpu-expected.txt
index b05c059..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_cpu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] lesserOrEqual float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_and.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_and.https.any_cpu-expected.txt
index 5b4922da..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_and.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_and.https.any_cpu-expected.txt
@@ -1,27 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] logicalAnd uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_not.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_not.https.any_cpu-expected.txt
deleted file mode 100644
index d22037d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_not.https.any_cpu-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] logicalNot uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 1D constant tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 1D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 2D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 3D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 4D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 5D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_or.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_or.https.any_cpu-expected.txt
index d7bb56a..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_or.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_or.https.any_cpu-expected.txt
@@ -1,27 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] logicalOr uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_xor.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_xor.https.any_cpu-expected.txt
index 46043f26..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_xor.https.any_cpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/logical_xor.https.any_cpu-expected.txt
@@ -1,27 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] logicalXor uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/equal.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/equal.https.any_npu-expected.txt
index 1395781..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/equal.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/equal.https.any_npu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] equal float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/greater.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/greater.https.any_npu-expected.txt
index 9acf726..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/greater.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/greater.https.any_npu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] greater float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_npu-expected.txt
index ab7740d..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_npu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] greaterOrEqual float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lesser.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lesser.https.any_npu-expected.txt
index d3d56a94..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lesser.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lesser.https.any_npu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] lesser float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_npu-expected.txt
index b05c059..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_npu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] lesserOrEqual float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_and.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_and.https.any_npu-expected.txt
index 5b4922da..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_and.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_and.https.any_npu-expected.txt
@@ -1,27 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] logicalAnd uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_not.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_not.https.any_npu-expected.txt
deleted file mode 100644
index d22037d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_not.https.any_npu-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] logicalNot uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 1D constant tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 1D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 2D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 3D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 4D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 5D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_or.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_or.https.any_npu-expected.txt
index d7bb56a..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_or.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_or.https.any_npu-expected.txt
@@ -1,27 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] logicalOr uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_xor.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_xor.https.any_npu-expected.txt
index 46043f26..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_xor.https.any_npu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/logical_xor.https.any_npu-expected.txt
@@ -1,27 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] logicalXor uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/equal.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/equal.https.any_gpu-expected.txt
index 1395781..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/equal.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/equal.https.any_gpu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] equal float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] equal float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/greater.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/greater.https.any_gpu-expected.txt
index 9acf726..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/greater.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/greater.https.any_gpu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] greater float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greater float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_gpu-expected.txt
index ab7740d..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/greater_or_equal.https.any_gpu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] greaterOrEqual float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] greaterOrEqual float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lesser.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lesser.https.any_gpu-expected.txt
index d3d56a94..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lesser.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lesser.https.any_gpu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] lesser float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesser float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_gpu-expected.txt
index b05c059..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/lesser_or_equal.https.any_gpu-expected.txt
@@ -1,51 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] lesserOrEqual float32 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float32 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] lesserOrEqual float16 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_and.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_and.https.any_gpu-expected.txt
index 5b4922da..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_and.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_and.https.any_gpu-expected.txt
@@ -1,27 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] logicalAnd uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalAnd uint8 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_not.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_not.https.any_gpu-expected.txt
deleted file mode 100644
index d22037d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_not.https.any_gpu-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] logicalNot uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 1D constant tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 1D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 2D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 3D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 4D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalNot uint8 5D tensor
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_or.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_or.https.any_gpu-expected.txt
index d7bb56a..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_or.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_or.https.any_gpu-expected.txt
@@ -1,27 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] logicalOr uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalOr uint8 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_xor.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_xor.https.any_gpu-expected.txt
index 46043f26..d2490db 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_xor.https.any_gpu-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/logical_xor.https.any_gpu-expected.txt
@@ -1,27 +1,3 @@
 This is a testharness.js-based test.
-[FAIL] logicalXor uint8 0D scalar
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 1D constant tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, output 'output' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 1D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 2D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 3D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 4D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 5D tensors
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 0D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 1D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 2D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 3D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
-[FAIL] logicalXor uint8 broadcast 4D to 4D
-  promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'a' data type uint8 must be one of [float32,float16,int32]."
 Harness: the test ran to completion.
 
diff --git a/third_party/catapult b/third_party/catapult
index ea769d2..aa341ec 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit ea769d2a2a7bedc43ac1baec43fdf7423047184e
+Subproject commit aa341ec41f6d475102eee85ddec60d403ef575cd
diff --git a/third_party/crossbench b/third_party/crossbench
index 675d2d6..0d4bbb0 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit 675d2d63880904c537f03a76f021c2c13e20e355
+Subproject commit 0d4bbb09c3901288772039f71f071897432d77b8
diff --git a/third_party/dawn b/third_party/dawn
index ba5fbf6..4d33621 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit ba5fbf6ca75113fc554651f2cd601ce911f3e6a1
+Subproject commit 4d33621217632e25d8e88e2f0dd2544596d129ac
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 749b3f1..d255a8d 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 749b3f1960ffd7575f6e7d5ecf1e90d6b9db673a
+Subproject commit d255a8d41e7a2fdc6b50fee69e70014f875d47ef
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 9520a83..d8f5635 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 9520a835b578aa11db9df1cf6651dbe5c3376887
+Subproject commit d8f563509959c9132c81f22536bff9edd4325093
diff --git a/third_party/googletest/src b/third_party/googletest/src
index e9092b1..7e2c425 160000
--- a/third_party/googletest/src
+++ b/third_party/googletest/src
@@ -1 +1 @@
-Subproject commit e9092b12dc3cf617d47578f13a1f64285cfa5b2f
+Subproject commit 7e2c425db2c2e024b2807bfe6d386f4ff068d0d6
diff --git a/third_party/libc++/src b/third_party/libc++/src
index 0e24258..cdae0b7 160000
--- a/third_party/libc++/src
+++ b/third_party/libc++/src
@@ -1 +1 @@
-Subproject commit 0e242589e53523da3fc2df7ee965f9534550dec5
+Subproject commit cdae0b78c315e58661273c8cd9119b460e68f98b
diff --git a/third_party/skia b/third_party/skia
index 07afa62..c408dae 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 07afa62babe959ab4e6759263490db189e2195c2
+Subproject commit c408daec0f9a46d5ed2f698a172ede3fd9f78551
diff --git a/third_party/webrtc b/third_party/webrtc
index 2ce86c4..e37b324 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 2ce86c46cefa1c8e62951f54c812b6faf69860ef
+Subproject commit e37b3246716888f66e859f17931f2cc7caedbbfd
diff --git a/tools/clang/trace_annotator/TraceAnnotator.cpp b/tools/clang/trace_annotator/TraceAnnotator.cpp
index a87cdd1..dbf296b1 100644
--- a/tools/clang/trace_annotator/TraceAnnotator.cpp
+++ b/tools/clang/trace_annotator/TraceAnnotator.cpp
@@ -199,7 +199,7 @@
       include_added_to.insert(r.getFilePath().str());
       // Add also copyright so that |test-expected.cc| passes presubmit.
       llvm::outs() << "include-user-header:::" << r.getFilePath()
-                   << ":::-1:::-1:::base/trace_event/base_tracing.h"
+                   << ":::-1:::-1:::base/trace_event/trace_event.h"
                    << "\n";
     }
     // Add the actual replacement.
diff --git a/tools/clang/trace_annotator/tests/test-expected.cc b/tools/clang/trace_annotator/tests/test-expected.cc
index 70e907d..d0cad0e6b 100644
--- a/tools/clang/trace_annotator/tests/test-expected.cc
+++ b/tools/clang/trace_annotator/tests/test-expected.cc
@@ -5,7 +5,7 @@
 #include <algorithm>
 #include <vector>
 
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 
 int no_body(int);  // No annotation
 
diff --git a/tools/metrics/histograms/metadata/actor/enums.xml b/tools/metrics/histograms/metadata/actor/enums.xml
index 57287df..db39848c 100644
--- a/tools/metrics/histograms/metadata/actor/enums.xml
+++ b/tools/metrics/histograms/metadata/actor/enums.xml
@@ -42,6 +42,7 @@
   <int value="21" label="CoordinatesOutOfBounds"/>
   <int value="22" label="ArgumentsInvalid"/>
   <int value="23" label="TaskPaused"/>
+  <int value="24" label="kExecutorDestroyed"/>
   <int value="100" label="NavigateInvalidUrl"/>
   <int value="200" label="ClickSuppressed"/>
   <int value="300" label="DragAndReleaseFromOffscreen"/>
diff --git a/tools/metrics/histograms/metadata/browser/histograms.xml b/tools/metrics/histograms/metadata/browser/histograms.xml
index c4361d9..47d39888 100644
--- a/tools/metrics/histograms/metadata/browser/histograms.xml
+++ b/tools/metrics/histograms/metadata/browser/histograms.xml
@@ -1372,6 +1372,18 @@
   </summary>
 </histogram>
 
+<histogram
+    name="BrowserRenderProcessHost.SpareProcessMaybeTakeAction.NavigationRequest"
+    enum="SpareProcessMaybeTakeAction" expires_after="2025-12-31">
+  <owner>gjc@google.com</owner>
+  <owner>chrome-loading@google.com</owner>
+  <summary>
+    Records what happens when an attempt is made to use a spare
+    RenderProcessHost during a navigation request. The UMA logs that either the
+    attempt succeeded or why it failed.
+  </summary>
+</histogram>
+
 <histogram name="BrowserRenderProcessHost.SpareProcessMaybeTakeTime"
     units="seconds" expires_after="2025-10-26">
   <owner>gjc@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/collaboration_service/enums.xml b/tools/metrics/histograms/metadata/collaboration_service/enums.xml
index cf8db7d..7f8905f9 100644
--- a/tools/metrics/histograms/metadata/collaboration_service/enums.xml
+++ b/tools/metrics/histograms/metadata/collaboration_service/enums.xml
@@ -26,6 +26,28 @@
 
 <enums>
 
+<!-- LINT.IfChange(CollaborationServiceFlowEvent) -->
+
+<enum name="CollaborationServiceFlowEvent">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Started"/>
+  <int value="2" label="Not signed in on a collaboration flow attempt"/>
+  <int value="3" label="Canceled: due to sign in requirement"/>
+  <int value="4" label="Signin and sync requirements met"/>
+  <int value="5" label="Signin not verified when finished"/>
+  <int value="6" label="Signin verified when finished"/>
+  <int value="7" label="Signin verified in observer"/>
+  <int value="8" label="Data sharing service ready when started"/>
+  <int value="9" label="Data sharing service ready observed"/>
+  <int value="10" label="Tab group sync service ready"/>
+  <int value="11" label="All services ready for flow"/>
+  <int value="12" label="Failure: Device policy disables signin"/>
+  <int value="13" label="Managed account signed in"/>
+  <int value="14" label="Account info is not ready upon signin"/>
+</enum>
+
+<!-- LINT.ThenChange(//components/collaboration/internal/metrics.h:CollaborationServiceFlowEvent) -->
+
 <!-- LINT.IfChange(CollaborationServiceJoinEntryPoint) -->
 
 <enum name="CollaborationServiceJoinEntryPoint">
@@ -44,7 +66,7 @@
 <enum name="CollaborationServiceJoinEvent">
   <int value="0" label="Unknown"/>
   <int value="1" label="Started"/>
-  <int value="2" label="Canceled - after sign in requirement"/>
+  <int value="2" label="Canceled - While on join screen"/>
   <int value="3" label="Canceled - due to sign in requirement"/>
   <int value="4" label="Not signed in on join attempt"/>
   <int value="5" label="Accepted - the join invitation"/>
@@ -131,8 +153,8 @@
 <enum name="CollaborationServiceShareOrManageEvent">
   <int value="0" label="Unknown"/>
   <int value="1" label="Started"/>
-  <int value="2" label="Canceled - due to sign in requirement"/>
-  <int value="3" label="Not signed in on share or manage attempt"/>
+  <int value="2" label="Not signed in on share or manage attempt"/>
+  <int value="3" label="Canceled - due to sign in requirement"/>
   <int value="4" label="Share dialog shown"/>
   <int value="5" label="Manage dialog shown"/>
   <int value="6" label="Collaboration group created"/>
diff --git a/tools/metrics/histograms/metadata/collaboration_service/histograms.xml b/tools/metrics/histograms/metadata/collaboration_service/histograms.xml
index cdc01b7..eaebf89 100644
--- a/tools/metrics/histograms/metadata/collaboration_service/histograms.xml
+++ b/tools/metrics/histograms/metadata/collaboration_service/histograms.xml
@@ -22,6 +22,14 @@
 
 <histograms>
 
+<variants name="CollaborationServiceFlow">
+  <variant name="JoinFlow" summary="Collaboration join flow"/>
+  <variant name="LeaveOrDeleteFlow"
+      summary="Collaboration leave or delete flow"/>
+  <variant name="ShareOrManageFlow"
+      summary="Collaboration share or manage flow"/>
+</variants>
+
 <variants name="CollaborationServiceStep">
   <variant name="AuthenticationInitToSuccess"
       summary="Authentication initiated to successful completion"/>
@@ -111,6 +119,20 @@
   </summary>
 </histogram>
 
+<histogram name="CollaborationService.{CollaborationServiceFlow}.Events"
+    enum="CollaborationServiceFlowEvent" expires_after="2025-10-11">
+  <owner>haileywang@google.com</owner>
+  <owner>chrome-tab-group-eng@google.com</owner>
+  <summary>
+    [ All platforms ] Tracks the type of collaboration flow event that can
+    happen when going through a collaboration flow.
+
+    Recorded when a collaboration event is triggered by the
+    CollaborationService.
+  </summary>
+  <token key="CollaborationServiceFlow" variants="CollaborationServiceFlow"/>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/extensions/enums.xml b/tools/metrics/histograms/metadata/extensions/enums.xml
index 2fcccbd1..90802d52 100644
--- a/tools/metrics/histograms/metadata/extensions/enums.xml
+++ b/tools/metrics/histograms/metadata/extensions/enums.xml
@@ -3356,6 +3356,15 @@
   <int value="1" label="Chrome Web Store"/>
 </enum>
 
+<!-- LINT.IfChange(LoadExtensionFlag) -->
+
+<enum name="LoadExtensionFlag">
+  <int value="0" label="LoadExtension"/>
+  <int value="1" label="DisableExtensionsExcept"/>
+</enum>
+
+<!-- LINT.ThenChange(/chrome/browser/extensions/extension_service.h:LoadExtensionFlag) -->
+
 <enum name="LoadRulesetResult">
   <int value="0" label="Load succeeded"/>
   <int value="1" label="Load failed - Invalid path"/>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index 7cdf3d0..5fdbc30 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -3685,14 +3685,15 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.LoadingFromCommandLineBlocked" enum="Boolean"
+<histogram name="Extensions.LoadingFromCommandLine" enum="LoadExtensionFlag"
     expires_after="2025-11-30">
   <owner>richche@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
-    Records when loading extensions via the &quot;--load-extension&quot; command
-    line switch is blocked. Emitted once per profile start while extensions are
-    loading.
+    Recorded when extension are loaded via the &quot;--load-extension&quot; or
+    &quot;--disable-extensions-except&quot; command line switch. Emitted once
+    per profile start while extensions are loading with flags present, and the
+    extension is loaded without being blocked.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/media/enums.xml b/tools/metrics/histograms/metadata/media/enums.xml
index e2159b7..32bb904e 100644
--- a/tools/metrics/histograms/metadata/media/enums.xml
+++ b/tools/metrics/histograms/metadata/media/enums.xml
@@ -495,7 +495,7 @@
       label="Disconnection error. The remote process dropped the callback.
              e.g. in case of crash."/>
   <int value="14" label="EME use is not allowed on unique origins."/>
-  <int value="15" label="Android: MediaDrmBridge creation failed."/>
+  <int value="15" label="(Obsolete) Android: MediaDrmBridge creation failed."/>
   <int value="16" label="Android: MediaCrypto not available."/>
   <int value="17" label="CrOs: Only one instance allowed."/>
   <int value="18" label="CrOs: Insufficient GPU memory available."/>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 5d11ca1d..ae291bb 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -2580,29 +2580,6 @@
   </summary>
 </histogram>
 
-<histogram name="Media.DocumentPictureInPicture.RequestedLargeInitialSize"
-    enum="BooleanOccurred" expires_after="2026-03-19">
-  <owner>steimel@chromium.org</owner>
-  <owner>media-dev-uma@chromium.org</owner>
-  <summary>
-    Recorded when a website opens a document picture-in-picture window. Records
-    `Occurred` if the website requested a window size larger than the maximum
-    allowed window size, otherwise records `Did not occur`.
-  </summary>
-</histogram>
-
-<histogram name="Media.DocumentPictureInPicture.RequestedLargeResize"
-    enum="BooleanOccurred" expires_after="2026-03-19">
-  <owner>steimel@chromium.org</owner>
-  <owner>media-dev-uma@chromium.org</owner>
-  <summary>
-    Recorded when a website resizes a document picture-in-picture window via the
-    `resizeTo()` or `resizeBy()` APIs. Records `Occurred` if the website
-    requested a window size larger than the maximum allowed window size,
-    otherwise records `Did not occur`.
-  </summary>
-</histogram>
-
 <histogram name="Media.DocumentPictureInPicture.RequestedSizeToScreenRatio"
     units="%" expires_after="2025-11-30">
   <owner>steimel@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/navigation/enums.xml b/tools/metrics/histograms/metadata/navigation/enums.xml
index 670186e..8a1d1be 100644
--- a/tools/metrics/histograms/metadata/navigation/enums.xml
+++ b/tools/metrics/histograms/metadata/navigation/enums.xml
@@ -464,7 +464,7 @@
   <int value="5" label="WillCommitWithoutUrlLoader"/>
 </enum>
 
-<!-- LINT.ThenChange(//content/browser/renderer_host/navigation_throttle_runner.h:Event)-->
+<!-- LINT.ThenChange(//content/browser/renderer_host/navigation_throttle_registry_impl.h:NavigationThrottleEvent)-->
 
 <!-- LINT.IfChange(NavigationTransitionCacheHitOrMissReason) -->
 
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 6819abb..d407136d 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -7478,6 +7478,38 @@
   </summary>
 </histogram>
 
+<histogram name="Net.TCPSocket.PortReuseTimeWindows.{IPType}.{Result}"
+    units="ms" expires_after="2025-12-01">
+  <owner>arichiv@chromium.org</owner>
+  <owner>awillia@chromium.org</owner>
+  <owner>katabolism-finch@google.com</owner>
+  <summary>
+    The time between the release of a socket on a particular local address/port
+    and its future re-use in another connection. The time for the last release
+    is cleared if the open is successful, and reused without overwriting if not.
+
+    Recorded once for each attempted re-use of an address/port for TCP on
+    Windows via TCPSocketWin::DoConnectComplete.
+
+    {IPType}
+
+    {Result}
+  </summary>
+  <token key="IPType">
+    <variant name="LinkLocal"
+        summary="Link-local IP addresses are used for communication on
+                 sub-network."/>
+    <variant name="Loopback"
+        summary="Loopback IP addresses are used for on-device network
+                 testing."/>
+    <variant name="Other" summary="All other IP addresses."/>
+  </token>
+  <token key="Result">
+    <variant name="Failure" summary="Socket failed to connect."/>
+    <variant name="Success" summary="Socket did connect."/>
+  </token>
+</histogram>
+
 <histogram
     name="Net.TrustTokens.NetErrorForFetchFailure.{TrustTokenOperationType}"
     enum="NetErrorCodes" expires_after="2023-06-06">
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 442a08e..67156f4 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v50.1/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "51eb61bc5ea08dfee24bdbbef80a60553f9c93ef",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/576110306936083fafbdab5594b96d25d4b4106d/trace_processor_shell.exe"
+            "hash": "f3146c0f8014bc96968ee4c97457c124efa6023d",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/5a1177aedb0839a3a3361ab7aca2db21a253bebe/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c",
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.cc b/ui/accessibility/platform/ax_platform_node_delegate.cc
index 7976964..8771069f 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate.cc
+++ b/ui/accessibility/platform/ax_platform_node_delegate.cc
@@ -6,6 +6,7 @@
 
 #include "base/containers/fixed_flat_set.h"
 #include "base/notreached.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_selection.h"
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index 1e74104a..8cb70fd 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -35,7 +35,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/typed_macros.h"
 #include "build/build_config.h"
 #include "net/filter/gzip_header.h"
 #include "skia/ext/image_operations.h"
diff --git a/ui/compositor/compositor_animation_observer.cc b/ui/compositor/compositor_animation_observer.cc
index e18d0fb..44fca12 100644
--- a/ui/compositor/compositor_animation_observer.cc
+++ b/ui/compositor/compositor_animation_observer.cc
@@ -9,7 +9,6 @@
 #include "base/notreached.h"
 #include "base/time/time.h"
 #include "base/time/time_override.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/trace_event.h"
 
 namespace ui {
diff --git a/ui/compositor/host_begin_frame_observer.cc b/ui/compositor/host_begin_frame_observer.cc
index 9dda576..64daee1 100644
--- a/ui/compositor/host_begin_frame_observer.cc
+++ b/ui/compositor/host_begin_frame_observer.cc
@@ -9,6 +9,8 @@
 #include "base/logging.h"
 #include "base/task/common/task_annotator.h"
 #include "base/time/time.h"
+#include "base/trace_event/trace_id_helper.h"
+#include "base/trace_event/typed_macros.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 
 namespace ui {
diff --git a/ui/display/manager/util/display_manager_util.h b/ui/display/manager/util/display_manager_util.h
index 58e2072..ec4a4e5 100644
--- a/ui/display/manager/util/display_manager_util.h
+++ b/ui/display/manager/util/display_manager_util.h
@@ -7,6 +7,7 @@
 
 #include <functional>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/flat_set.h"
diff --git a/ui/file_manager/file_manager/foreground/js/dialog_action_controller.ts b/ui/file_manager/file_manager/foreground/js/dialog_action_controller.ts
index e747f9c7..3923b76 100644
--- a/ui/file_manager/file_manager/foreground/js/dialog_action_controller.ts
+++ b/ui/file_manager/file_manager/foreground/js/dialog_action_controller.ts
@@ -290,7 +290,13 @@
       return null;  // No specific filter selected.
     }
     return new RegExp(
-        '\\.(' + this.fileTypes_[selectedIndex - 1]!.extensions.join('|') +
+        '\\.(' +
+            this.fileTypes_[selectedIndex - 1]!
+                .extensions
+                // RegExp.escape is available since M136, but Typescript doesn't
+                // recognize it yet, hence the "as any" below.
+                .map(ext => (RegExp as any).escape(ext))
+                .join('|') +
             ')$',
         'i');
   }
diff --git a/ui/file_manager/file_manager/foreground/js/dialog_action_controller_unittest.ts b/ui/file_manager/file_manager/foreground/js/dialog_action_controller_unittest.ts
new file mode 100644
index 0000000..ac9cc5b
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/dialog_action_controller_unittest.ts
@@ -0,0 +1,91 @@
+// 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 {assertFalse, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
+
+import {MockVolumeManager} from '../../background/js/mock_volume_manager.js';
+import {DialogType} from '../../state/state.js';
+
+import {DialogActionController} from './dialog_action_controller.js';
+import {FileFilter} from './directory_contents.js';
+import {FakeFileSelectionHandler} from './fake_file_selection_handler.js';
+import {LaunchParam} from './launch_param.js';
+import {createFakeDirectoryModel} from './mock_directory_model.js';
+import type {NamingController} from './naming_controller.js';
+import {DialogFooter} from './ui/dialog_footer.js';
+
+// Mock the same DOM structure as required by DialogFooter constructor.
+function constructFooterElement(): HTMLElement {
+  const footerElement = document.createElement('div');
+
+  const selectElement = document.createElement('div');
+  selectElement.className = 'file-type';
+  const options = document.createElement('div');
+  options.className = 'options';
+  const label = document.createElement('span');
+  selectElement.appendChild(label);
+  selectElement.appendChild(options);
+  footerElement.appendChild(selectElement);
+
+  const okButton = document.createElement('button');
+  okButton.className = 'ok';
+  const buttonLabel = document.createElement('span');
+  okButton.appendChild(buttonLabel);
+  footerElement.appendChild(okButton);
+
+  const cancelButton = document.createElement('button');
+  cancelButton.className = 'cancel';
+  footerElement.appendChild(cancelButton);
+
+  const newFolderButton = document.createElement('button');
+  newFolderButton.id = 'new-folder-button';
+  footerElement.appendChild(newFolderButton);
+
+  document.body.appendChild(footerElement);
+  const newFolderCommand = document.createElement('command');
+  newFolderCommand.id = 'new-folder';
+  document.body.appendChild(newFolderCommand);
+
+  return footerElement;
+}
+
+export function testFilterFilesWithSpecialCharactersExtension() {
+  const dialogType = DialogType.SELECT_SAVEAS_FILE;
+  const volumeManager = new MockVolumeManager();
+  const fileSelectionHandler = new FakeFileSelectionHandler();
+  const fileFilter = new FileFilter(volumeManager);
+  const footerElement = constructFooterElement();
+
+  new DialogActionController(
+      dialogType,
+      new DialogFooter(
+          dialogType, footerElement, document.createElement('cr-input')),
+      createFakeDirectoryModel(),
+      volumeManager,
+      fileFilter,
+      {} as NamingController,
+      fileSelectionHandler,
+      new LaunchParam({
+        typeList: [
+          {extensions: ['*'], description: 'any', selected: false},
+          {extensions: ['c++', 'h++'], description: 'custom', selected: true},
+        ],
+      }),
+  );
+
+  // The constructor will call onFileTypeFilterChanged_(), which will call
+  // regexpForCurrentFilter_() and add the regex to the `fileFilter`, that's
+  // where we can test the regular expression.
+  // Remove all other filters to make sure we are testing fileType filter.
+  fileFilter.removeFilter('hidden');
+  fileFilter.removeFilter('android_hidden');
+  fileFilter.removeFilter('android_download');
+
+  const entry1 = {name: 'a.c++', isDirectory: false} as any as Entry;
+  const entry2 = {name: 'b.h++', isDirectory: false} as any as Entry;
+  const entry3 = {name: 'c.html', isDirectory: false} as any as Entry;
+  assertTrue(fileFilter.filter(entry1));
+  assertTrue(fileFilter.filter(entry2));
+  assertFalse(fileFilter.filter(entry3));
+}
diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni
index 28b1fb4..59e5501 100644
--- a/ui/file_manager/file_names.gni
+++ b/ui/file_manager/file_names.gni
@@ -429,6 +429,7 @@
   "file_manager/foreground/js/actions_model_unittest.ts",
   "file_manager/foreground/js/banner_controller_unittest.ts",
   "file_manager/foreground/js/banner_util_unittest.ts",
+  "file_manager/foreground/js/dialog_action_controller_unittest.ts",
   "file_manager/foreground/js/directory_contents_unittest.ts",
   "file_manager/foreground/js/directory_model_unittest.ts",
   "file_manager/foreground/js/empty_folder_controller_unittest.ts",
diff --git a/ui/gfx/x/connection.cc b/ui/gfx/x/connection.cc
index 6d6a9ba..9c7f4aaf 100644
--- a/ui/gfx/x/connection.cc
+++ b/ui/gfx/x/connection.cc
@@ -410,8 +410,7 @@
     }
     events_.pop_front();
   }
-  // Move an event from XCB's internal queue to our queue, if available.
-  return ReadResponse(/*queued=*/false);
+  return false;
 }
 
 int Connection::GetFd() {
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index cba47e8b..c9f3520 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -22,6 +22,7 @@
 #include <optional>
 #include <string>
 #include <string_view>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
diff --git a/ui/ozone/platform/drm/gpu/fake_drm_device.h b/ui/ozone/platform/drm/gpu/fake_drm_device.h
index 45915cb..c3159ce 100644
--- a/ui/ozone/platform/drm/gpu/fake_drm_device.h
+++ b/ui/ozone/platform/drm/gpu/fake_drm_device.h
@@ -19,6 +19,7 @@
 #include <optional>
 #include <set>
 #include <tuple>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/flat_map.h"
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_v3_unittest.cc b/ui/ozone/platform/wayland/host/zwp_text_input_v3_unittest.cc
index 93485580..65400a1 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_v3_unittest.cc
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_v3_unittest.cc
@@ -11,6 +11,7 @@
 #include <string_view>
 
 #include "base/strings/utf_offset_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ime/surrounding_text_tracker.h"
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate.cc b/ui/views/accessibility/view_ax_platform_node_delegate.cc
index 987a4a5..27a43fc 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate.cc
@@ -17,6 +17,7 @@
 #include "base/i18n/rtl.h"
 #include "base/lazy_instance.h"
 #include "base/memory/raw_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "build/build_config.h"
diff --git a/ui/views/controls/editable_combobox/editable_combobox_unittest.cc b/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
index 5be8824..80c58a1 100644
--- a/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
+++ b/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
@@ -13,6 +13,7 @@
 
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
diff --git a/ui/views/examples/animation_example.cc b/ui/views/examples/animation_example.cc
index 84097a1d..90936c46 100644
--- a/ui/views/examples/animation_example.cc
+++ b/ui/views/examples/animation_example.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_header_macros.h"
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 9799a1e..4d5cff80 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -18,7 +18,6 @@
 #include "base/observer_list.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/current_thread.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "ui/accessibility/platform/ax_platform.h"
diff --git a/ui/views/win/hwnd_util.cc b/ui/views/win/hwnd_util.cc
index 3b6e2b7c..0f42fdd 100644
--- a/ui/views/win/hwnd_util.cc
+++ b/ui/views/win/hwnd_util.cc
@@ -8,7 +8,7 @@
 
 #include "base/i18n/rtl.h"
 #include "base/task/current_thread.h"
-#include "base/trace_event/base_tracing.h"
+#include "base/trace_event/trace_event.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
diff --git a/url/android/gurl_java_test_helper.cc b/url/android/gurl_java_test_helper.cc
index b710c09..e737d44 100644
--- a/url/android/gurl_java_test_helper.cc
+++ b/url/android/gurl_java_test_helper.cc
@@ -4,6 +4,8 @@
 
 #include <stddef.h>
 
+#include <sstream>
+
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/test/icu_test_util.h"
diff --git a/url/android/origin_java_test_helper.cc b/url/android/origin_java_test_helper.cc
index da9bcfd..7851dffb 100644
--- a/url/android/origin_java_test_helper.cc
+++ b/url/android/origin_java_test_helper.cc
@@ -4,6 +4,8 @@
 
 #include <stddef.h>
 
+#include <sstream>
+
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "url/gurl.h"
diff --git a/url/gurl.cc b/url/gurl.cc
index e8fdf74..278e3a4 100644
--- a/url/gurl.cc
+++ b/url/gurl.cc
@@ -21,8 +21,8 @@
 #include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/strings/string_util.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/memory_usage_estimator.h"
+#include "base/trace_event/trace_event.h"
 #include "url/url_canon_stdstring.h"
 #include "url/url_util.h"
 
diff --git a/url/origin.cc b/url/origin.cc
index ba58f21..1176262f 100644
--- a/url/origin.cc
+++ b/url/origin.cc
@@ -27,8 +27,8 @@
 #include "base/debug/crash_logging.h"
 #include "base/pickle.h"
 #include "base/strings/strcat.h"
-#include "base/trace_event/base_tracing.h"
 #include "base/trace_event/memory_usage_estimator.h"
+#include "base/trace_event/trace_event.h"
 #include "base/unguessable_token.h"
 #include "url/gurl.h"
 #include "url/scheme_host_port.h"