diff --git a/BUILD.gn b/BUILD.gn
index 6b729528..6617382 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -199,6 +199,7 @@
       "//third_party/blink/renderer/platform/wtf:wtf_unittests",
       "//third_party/libjingle_xmpp:libjingle_xmpp_unittests",
       "//tools/imagediff",
+      "//tools/memory:all",
       "//ui/display:display_unittests",
       "//ui/gl:gl_unittests",
       "//ui/latency:latency_unittests",
@@ -436,10 +437,6 @@
     ]
   }
 
-  if (is_linux || is_chromeos || is_android || is_mac) {
-    deps += [ "//tools/memory/partition_allocator:all" ]
-  }
-
   if (is_chromeos_ash) {
     deps += [
       "//ash:ash_unittests",
@@ -1031,10 +1028,10 @@
     if (use_v4l2_codec || use_vaapi) {
       data_deps += [
         "//components/chromeos_camera:jpeg_encode_accelerator_unittest",
-        "//media/gpu:video_decode_accelerator_perf_tests",
-        "//media/gpu:video_decode_accelerator_tests",
-        "//media/gpu:video_encode_accelerator_perf_tests",
-        "//media/gpu:video_encode_accelerator_tests",
+        "//media/gpu/test/:video_decode_accelerator_perf_tests",
+        "//media/gpu/test/:video_decode_accelerator_tests",
+        "//media/gpu/test/:video_encode_accelerator_perf_tests",
+        "//media/gpu/test/:video_encode_accelerator_tests",
         "//media/gpu/chromeos:image_processor_test",
       ]
       if (use_vaapi) {
diff --git a/DEPS b/DEPS
index b6b3534..af17a5d9 100644
--- a/DEPS
+++ b/DEPS
@@ -304,15 +304,15 @@
   # 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': '89849a32bdd7b8260bea688e9708729818576c16',
+  'skia_revision': '528ff00a3637f6fef419b69392c803f8f2a1e778',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'fe05deddba8ac73e115a9327728219b609aec6a5',
+  'v8_revision': '5f7f064aee9828f22e29abdc45b63e6c9b854298',
   # 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': 'afc1e5dcc130971988aa012a1d8be5412a7df88f',
+  'angle_revision': 'fbb16d64636b352e2a6c68b8dfc8bbd5e5918585',
   # 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 Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:12.20230327.0.1',
+  'fuchsia_version': 'version:12.20230327.2.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -375,7 +375,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '9d41f74af7343a821aa418f8140e204814f92139',
+  'catapult_revision': 'e838386fa052bd38ad2f7f46fd04e451ce110324',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # 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': 'e6e6905f99923a12713b1ee0443fcccf4eaef736',
+  'devtools_frontend_revision': 'a09c4de27878ff6f526c3b78f53bf99e6495f1d3',
   # 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.
@@ -495,7 +495,7 @@
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       '79a510511b03dc9a9f378ebe07c18c622b5bf349',
+  'libcxx_revision':       '26ace673c4b9c7a352fa9db182256bb341b6c44f',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:41fef642de70ecdcaaa26be96d56a0398f95abd4',
@@ -794,7 +794,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'f6c0952bdff6acffb8d5d143b07be668c236359a',
+    '3935d163b8c4d7775a19b87a678849992e18f255',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1202,7 +1202,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'de3428b38fe70dd593c66c0988472f649f948b36',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8b9b1646b44ebf6946de4392abeaf6d7e14c15a0',
       'condition': 'checkout_chromeos',
   },
 
@@ -1234,13 +1234,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd9717259ee7ecbe8de97b75a264f5edc18224ab2',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '33a8f072364d819b69cfb45958e17aca021150ad',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '50e5383c45b884a15b380dbb62a86e9a6a2e758a',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '1edfd44e2837c5219e24e004e31602c19ca6a9c2',
     'condition': 'checkout_src_internal',
   },
 
@@ -1706,7 +1706,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '084059ccadd42d5cee0c03931d09dd96ed6b7a99',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'e904de5554bd8af597344c98aefd940a6a7d00fb',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1851,7 +1851,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@d56f491466de515bec780ad1d70e53e523ab6e65',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@2f3f68dfda8b45bb661cf28637c09509917c7a4d',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1888,10 +1888,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '904c74af6fee0530ce33434974aa0df6316fdef1',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '7e014d4d99cdacd0c08a711d379fcb7886ed66d2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '8e781a1bb17647d249ac233dfd99ba92b3c69fcb',
+    Var('webrtc_git') + '/src.git' + '@' + '90d5e7d65520656ddec367cd454bffc16d55c423',
 
   # 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.
@@ -1925,7 +1925,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/linux-amd64',
-          'version': 'iQ7zKud-gha6r9hEdwqYWRdOpeAs6gFfDxnviDUt4FQC',
+          'version': 'HkmOI1AX5KQdM4e3VR1v5kN_kJWirqdbUeY0lEj93FcC',
         },
       ],
       'dep_type': 'cipd',
@@ -1935,7 +1935,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'we56UJIWxJJ2GkQ_ne0o3oGAr7FBJa5T5Jr1xguLn-gC',
+          'version': 'pUOVquC14y84RkNMzEAPw_7tztey-kvH7KBwGsFViuIC',
         },
       ],
       'dep_type': 'cipd',
@@ -1946,7 +1946,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': '9Wfje1bt82IO9pJokAt9lboy59X_Pe-s0b4EpmH7RT4C',
+          'version': 'enC5EfEkxcrBLcpjtbBzQmOsnYRzN43BtNoPNzoyjekC',
         },
       ],
       'dep_type': 'cipd',
@@ -1957,7 +1957,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-arm64',
-          'version': 'zihT2Lk2afg0XzIZozuGcZXWv7RJujaDEi_6q7QL4DgC',
+          'version': 'Jh19SHnigVXYxpk7Fp4ZDMF_ZvLpQUie2NMaK5aEISMC',
         },
       ],
       'dep_type': 'cipd',
@@ -1968,7 +1968,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': Var('chrome_git') + '/chrome/src-internal.git@afe6094e9777a02fa5a5784d4275860cc598de61',
+    'url': Var('chrome_git') + '/chrome/src-internal.git@24d159fca4f5a3e4a816bfab61767356a578dfa5',
     'condition': 'checkout_src_internal',
   },
 
@@ -2016,6 +2016,17 @@
     'dep_type': 'cipd',
   },
 
+  'src/ash/webui/personalization_app/resources': {
+    'packages': [
+      {
+        'package': 'chromeos_internal/assistant/time_of_day',
+        'version': '7okw0Y1HdRp76vhM8AGsWOloCQ83hwMd7Y1k2sDYMJcC',
+      },
+    ],
+    'condition': 'checkout_chromeos and checkout_src_internal',
+    'dep_type': 'cipd',
+  },
+
   'src/ash/webui/projector_app/resources/prod': {
     'packages': [
       {
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 86e53e01..6d37b2a 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -6844,6 +6844,9 @@
 
 def CheckLibcxxRevisionsMatch(input_api, output_api):
     """Check to make sure the libc++ version matches across deps files."""
+    # Disable check for changes to sub-repositories.
+    if input_api.PresubmitLocalPath() != input_api.change.RepositoryRoot():
+      return []
 
     DEPS_FILES = [ 'DEPS', 'buildtools/deps_revisions.gni' ]
 
diff --git a/android_webview/java/src/org/chromium/android_webview/metrics/AwMetricsLogUploader.java b/android_webview/java/src/org/chromium/android_webview/metrics/AwMetricsLogUploader.java
index c695564..c3dc893 100644
--- a/android_webview/java/src/org/chromium/android_webview/metrics/AwMetricsLogUploader.java
+++ b/android_webview/java/src/org/chromium/android_webview/metrics/AwMetricsLogUploader.java
@@ -95,6 +95,7 @@
         private final boolean mUseDefaultUploadQos;
         private final @NonNull byte[] mData;
         private final CompletableFuture<Integer> mResult;
+        private final AtomicBoolean mPosted = new AtomicBoolean();
 
         public MetricsLogUploaderServiceConnection(boolean useDefaultUploadQos,
                 @NonNull byte[] data, @NonNull CompletableFuture<Integer> resultFuture) {
@@ -105,6 +106,11 @@
 
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
+            // We want to avoid re-posting if the service connection dies
+            // and reconnects.
+            if (mPosted.getAndSet(true)) {
+                return;
+            }
             // onServiceConnected is called on the app main looper so post it to a background thread
             // for execution. No need to enforce the order in which the logs are sent to the service
             // as this isn't required/enforced by UMA.
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 49886ae..1e68fd5 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -70,7 +70,6 @@
 #include "services/network/public/cpp/features.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/common/switches.h"
 #include "tools/v8_context_snapshot/buildflags.h"
 #include "ui/base/ui_base_paths.h"
 #include "ui/base/ui_base_switches.h"
@@ -316,13 +315,6 @@
     features.DisableIfNotSet(::features::kFedCm);
   }
 
-  // Enable Event.path on Beta and Stable. The feature has been deprecated and
-  // removed on other platforms, but needs more time on WebView.
-  // See crbug.com/1277431 for more details.
-  if (version_info::android::GetChannel() >= version_info::Channel::BETA) {
-    cl->AppendSwitch(blink::switches::kEventPathEnabledByDefault);
-  }
-
   android_webview::RegisterPathProvider();
 
   // Used only if the argument filter is enabled in tracing config,
diff --git a/ash/accelerators/accelerator_commands.cc b/ash/accelerators/accelerator_commands.cc
index 7b69894d..1fdd21e8 100644
--- a/ash/accelerators/accelerator_commands.cc
+++ b/ash/accelerators/accelerator_commands.cc
@@ -1571,7 +1571,8 @@
 }
 
 void VolumeMute() {
-  CrasAudioHandler::Get()->SetOutputMute(true);
+  CrasAudioHandler::Get()->SetOutputMute(
+      true, CrasAudioHandler::AudioSettingsChangeSource::kAccelerator);
 }
 
 void VolumeUp() {
diff --git a/ash/public/cpp/window_properties.cc b/ash/public/cpp/window_properties.cc
index f36af76..d3d9f96 100644
--- a/ash/public/cpp/window_properties.cc
+++ b/ash/public/cpp/window_properties.cc
@@ -70,5 +70,6 @@
 DEFINE_UI_CLASS_PROPERTY_KEY(ResizeShadowType,
                              kResizeShadowTypeKey,
                              ResizeShadowType::kUnlock)
+DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kDeskGuidKey, nullptr)
 
 }  // namespace ash
diff --git a/ash/public/cpp/window_properties.h b/ash/public/cpp/window_properties.h
index 016e36d..711a86f 100644
--- a/ash/public/cpp/window_properties.h
+++ b/ash/public/cpp/window_properties.h
@@ -200,6 +200,11 @@
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<gfx::Rect*>* const
     kWindowPipResizeHandleBoundsKey;
 
+// A property key to indicate a desk guid of a workspace this window belongs
+// to.
+ASH_PUBLIC_EXPORT extern const aura::WindowProperty<std::string*>* const
+    kDeskGuidKey;
+
 // Alphabetical sort.
 
 }  // namespace ash
diff --git a/ash/shelf/drag_window_from_shelf_controller_unittest.cc b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
index 592902a2..bdecbff 100644
--- a/ash/shelf/drag_window_from_shelf_controller_unittest.cc
+++ b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
@@ -1575,6 +1575,7 @@
   // does nothing.
   GetEventGenerator()->PressTouch(drag_point_under_float);
   GetEventGenerator()->MoveTouchBy(0, -200);
+  GetEventGenerator()->ReleaseTouch();
   EXPECT_FALSE(GetDragWindowFromShelfController()->drag_started());
 }
 
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 1a518201..a17d081 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -244,6 +244,32 @@
   return app_list_visible ? -offset : offset;
 }
 
+// The floated window is the dragged window if it is not tucked, magnetized to
+// the bottom, and above the event.
+bool CanDragFloatedWindowFromShelf(aura::Window* floated_window,
+                                   const gfx::Point& location_in_screen) {
+  if (!Shell::Get()->float_controller()->IsFloatedWindowAlignedWithShelf(
+          floated_window)) {
+    return false;
+  }
+  DCHECK(floated_window->IsVisible());
+  const gfx::Rect floated_window_bounds = floated_window->GetBoundsInScreen();
+  return location_in_screen.x() <= floated_window_bounds.right() &&
+         location_in_screen.x() >= floated_window_bounds.x();
+}
+
+// Checks if the only visible window is floated and not the dragged window, in
+// which case we treat it as swipe home to overview.
+bool IsDragOverShelfWithFloatedWindow(const gfx::Point& location_in_screen) {
+  aura::Window* top_window = window_util::GetTopNonFloatedWindow();
+  if (top_window && top_window->IsVisible()) {
+    return false;
+  }
+  aura::Window* floated_window = window_util::GetFloatedWindowForActiveDesk();
+  return floated_window &&
+         !CanDragFloatedWindowFromShelf(floated_window, location_in_screen);
+}
+
 // Returns the window that can be dragged from shelf into home screen or
 // overview at |location_in_screen|. Returns nullptr if there is no such
 // window.
@@ -267,20 +293,11 @@
     return nullptr;
   }
 
-  // Checks for a floated window. The floated window is the dragged window if it
-  // is not tucked, magnetized to the bottom, and above the event.
   if (aura::Window* floated_window =
-          window_util::GetFloatedWindowForActiveDesk()) {
-    if (Shell::Get()->float_controller()->IsFloatedWindowAlignedWithShelf(
-            floated_window)) {
-      const gfx::Rect floated_window_bounds =
-          floated_window->GetBoundsInScreen();
-      if (location_in_screen.x() <= floated_window_bounds.right() &&
-          location_in_screen.x() >= floated_window_bounds.x()) {
-        DCHECK(floated_window->IsVisible());
-        return floated_window;
-      }
-    }
+          window_util::GetFloatedWindowForActiveDesk();
+      floated_window &&
+      CanDragFloatedWindowFromShelf(floated_window, location_in_screen)) {
+    return floated_window;
   }
 
   // If split view mode is not active, use the first MRU window.
@@ -2448,8 +2465,10 @@
   // In tablet mode, let swipe_home_to_overview_controller handle swipe up
   // gestures on the home launcher screen.
   if (Shell::Get()->IsInTabletMode() &&
-      Shell::Get()->app_list_controller()->IsVisible(display_.id()) &&
-      Shell::Get()->app_list_controller()->GetTargetVisibility(display_.id()) &&
+      ((Shell::Get()->app_list_controller()->IsVisible(display_.id()) &&
+        Shell::Get()->app_list_controller()->GetTargetVisibility(
+            display_.id())) ||
+       IsDragOverShelfWithFloatedWindow(gesture_in_screen.location())) &&
       scroll_y_hint < 0) {
     drag_status_ = kDragHomeToOverviewInProgress;
     swipe_home_to_overview_controller_ =
diff --git a/ash/style/color_palette_controller.cc b/ash/style/color_palette_controller.cc
index 88326966..2d747a76 100644
--- a/ash/style/color_palette_controller.cc
+++ b/ash/style/color_palette_controller.cc
@@ -71,7 +71,9 @@
       WallpaperControllerImpl* wallpaper_controller)
       : wallpaper_controller_(wallpaper_controller),
         dark_light_mode_controller_(dark_light_mode_controller) {
+    dark_light_observation_.Observe(dark_light_mode_controller);
     wallpaper_observation_.Observe(wallpaper_controller);
+
     wallpaper_color_[ColorMode::kDark] = SK_ColorTRANSPARENT;
     wallpaper_color_[ColorMode::kLight] = SK_ColorTRANSPARENT;
   }
@@ -139,12 +141,25 @@
     return seed;
   }
 
+  ColorPaletteSeed GetCurrentSeed() const override {
+    const auto* session = GetActiveUserSession();
+    if (!session) {
+      return {};
+    }
+
+    return GetColorPaletteSeed(AccountFromSession(session));
+  }
+
   bool UsesWallpaperSeedColor(const AccountId& account_id) const override {
     // Scheme tracks if wallpaper color is used.
     return GetColorScheme(account_id) != ColorScheme::kStatic;
   }
 
   ColorScheme GetColorScheme(const AccountId& account_id) const override {
+    if (!chromeos::features::IsJellyEnabled()) {
+      // Pre-Jelly, this is always Tonal Spot.
+      return ColorScheme::kTonalSpot;
+    }
     PrefService* pref_service = GetUserPrefService(account_id);
     if (!pref_service) {
       DVLOG(1)
@@ -249,6 +264,9 @@
 
   base::flat_map<ui::ColorProviderManager::ColorMode, SkColor> wallpaper_color_;
 
+  base::ScopedObservation<DarkLightModeController, ColorModeObserver>
+      dark_light_observation_{this};
+
   base::ScopedObservation<WallpaperController, WallpaperControllerObserver>
       wallpaper_observation_{this};
 
diff --git a/ash/style/color_palette_controller.h b/ash/style/color_palette_controller.h
index b7a82502..0edfc4f 100644
--- a/ash/style/color_palette_controller.h
+++ b/ash/style/color_palette_controller.h
@@ -5,6 +5,8 @@
 #ifndef ASH_STYLE_COLOR_PALETTE_CONTROLLER_H_
 #define ASH_STYLE_COLOR_PALETTE_CONTROLLER_H_
 
+#include <tuple>
+
 #include "ash/ash_export.h"
 #include "base/containers/span.h"
 #include "base/observer_list_types.h"
@@ -13,6 +15,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/color/color_provider_manager.h"
+#include "ui/gfx/color_palette.h"
 
 class PrefRegistrySimple;
 
@@ -35,11 +38,17 @@
 // palette.
 struct ASH_EXPORT ColorPaletteSeed {
   // The color which the palette is generated from.
-  SkColor seed_color;
+  SkColor seed_color = gfx::kGoogleBlue400;
   // The type of palette which is being generated.
-  ColorScheme scheme;
+  ColorScheme scheme = ColorScheme::kStatic;
   // Dark or light palette.
-  ui::ColorProviderManager::ColorMode color_mode;
+  ui::ColorProviderManager::ColorMode color_mode =
+      ui::ColorProviderManager::ColorMode::kLight;
+
+  bool operator==(const ColorPaletteSeed& other) const {
+    return std::tie(seed_color, scheme, color_mode) ==
+           std::tie(other.seed_color, other.scheme, other.color_mode);
+  }
 };
 
 // Samples of color schemes for the tri-color scheme previews.
@@ -108,6 +117,9 @@
   virtual ColorPaletteSeed GetColorPaletteSeed(
       const AccountId& account_id) const = 0;
 
+  // Returns the current seed for the current user.
+  virtual ColorPaletteSeed GetCurrentSeed() const = 0;
+
   // Returns true if using a color scheme based on the current wallpaper.
   virtual bool UsesWallpaperSeedColor(const AccountId& account_id) const = 0;
 
diff --git a/ash/style/color_palette_controller_unittest.cc b/ash/style/color_palette_controller_unittest.cc
index 7bd5ae7..78fbc31 100644
--- a/ash/style/color_palette_controller_unittest.cc
+++ b/ash/style/color_palette_controller_unittest.cc
@@ -8,7 +8,12 @@
 #include "ash/style/dark_light_mode_controller_impl.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
+#include "ash/wallpaper/wallpaper_controller_test_api.h"
+#include "ash/wallpaper/wallpaper_utils/wallpaper_calculated_colors.h"
 #include "base/functional/callback_helpers.h"
+#include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/skia/include/core/SkColor.h"
 
 namespace ash {
@@ -18,6 +23,18 @@
 const char kUser[] = "user@gmail.com";
 const AccountId kAccountId = AccountId::FromUserEmailGaiaId(kUser, kUser);
 
+// A nice magenta that is in the acceptable lightness range for dark and light.
+// Hue: 281, Saturation: 100, Lightness: 50%.
+constexpr SkColor kKMeanColor = SkColorSetRGB(0xae, 0x00, 0xff);
+
+class MockPaletteObserver : public ColorPaletteController::Observer {
+ public:
+  MOCK_METHOD(void,
+              OnColorPaletteChanging,
+              (const ColorPaletteSeed& seed),
+              (override));
+};
+
 }  // namespace
 
 class ColorPaletteControllerTest : public NoSessionAshTestBase {
@@ -26,24 +43,31 @@
     NoSessionAshTestBase::SetUp();
     GetSessionControllerClient()->Reset();
     GetSessionControllerClient()->AddUserSession(kAccountId, kUser);
-    color_palette_controller_ = ColorPaletteController::Create(
-        Shell::Get()->dark_light_mode_controller(),
-        Shell::Get()->wallpaper_controller());
+    dark_light_mode_controller_ = Shell::Get()->dark_light_mode_controller();
+    wallpaper_controller_ = Shell::Get()->wallpaper_controller();
+    color_palette_controller_ = Shell::Get()->color_palette_controller();
   }
 
-  void TearDown() override {
-    // Must release the controller before Shell is destructed.
-    color_palette_controller_.reset();
-
-    NoSessionAshTestBase::TearDown();
-  }
+  void TearDown() override { NoSessionAshTestBase::TearDown(); }
 
   ColorPaletteController* color_palette_controller() {
-    return color_palette_controller_.get();
+    return color_palette_controller_;
+  }
+
+  DarkLightModeControllerImpl* dark_light_controller() {
+    return dark_light_mode_controller_;
+  }
+
+  WallpaperControllerImpl* wallpaper_controller() {
+    return wallpaper_controller_;
   }
 
  private:
-  std::unique_ptr<ColorPaletteController> color_palette_controller_;
+  base::raw_ptr<DarkLightModeControllerImpl>
+      dark_light_mode_controller_;                               // unowned
+  base::raw_ptr<WallpaperControllerImpl> wallpaper_controller_;  // unowned
+
+  base::raw_ptr<ColorPaletteController> color_palette_controller_;
 };
 
 TEST_F(ColorPaletteControllerTest, ExpectedEmptyValues) {
@@ -53,7 +77,24 @@
             color_palette_controller()->GetStaticColor(kAccountId));
 }
 
+TEST_F(ColorPaletteControllerTest, SetColorScheme_JellyDisabled_AlwaysTonal) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(chromeos::features::kJelly);
+
+  color_palette_controller()->SetColorScheme(ColorScheme::kStatic, kAccountId,
+                                             base::DoNothing());
+  EXPECT_EQ(ColorScheme::kTonalSpot,
+            color_palette_controller()->GetColorPaletteSeed(kAccountId).scheme);
+
+  color_palette_controller()->SetColorScheme(ColorScheme::kExpressive,
+                                             kAccountId, base::DoNothing());
+  EXPECT_EQ(ColorScheme::kTonalSpot,
+            color_palette_controller()->GetColorPaletteSeed(kAccountId).scheme);
+}
+
 TEST_F(ColorPaletteControllerTest, SetColorScheme) {
+  base::test::ScopedFeatureList feature_list(chromeos::features::kJelly);
+
   ColorScheme color_scheme = ColorScheme::kExpressive;
 
   color_palette_controller()->SetColorScheme(color_scheme, kAccountId,
@@ -69,6 +110,7 @@
 }
 
 TEST_F(ColorPaletteControllerTest, SetStaticColor) {
+  base::test::ScopedFeatureList feature_list(chromeos::features::kJelly);
   SkColor static_color = SK_ColorGRAY;
 
   color_palette_controller()->SetStaticColor(static_color, kAccountId,
@@ -84,4 +126,43 @@
   EXPECT_EQ(static_color, color_palette_seed.seed_color);
 }
 
+// If the Jelly flag is off, we always return the KMeans color from the
+// wallpaper controller regardless of scheme.
+TEST_F(ColorPaletteControllerTest, SetStaticColor_JellyDisabled_AlwaysKMeans) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(chromeos::features::kJelly);
+
+  WallpaperControllerTestApi wallpaper(wallpaper_controller());
+  wallpaper.SetCalculatedColors(
+      WallpaperCalculatedColors({}, kKMeanColor, SK_ColorWHITE));
+
+  color_palette_controller()->SetColorScheme(ColorScheme::kStatic, kAccountId,
+                                             base::DoNothing());
+  color_palette_controller()->SetStaticColor(SK_ColorRED, kAccountId,
+                                             base::DoNothing());
+
+  // TODO(skau): Check that this matches kKMean after color blending has been
+  // moved.
+  EXPECT_NE(
+      SK_ColorWHITE,
+      color_palette_controller()->GetColorPaletteSeed(kAccountId).seed_color);
+}
+
+TEST_F(ColorPaletteControllerTest, ColorModeTriggersObserver) {
+  // Initialize Dark mode to a known state.
+  dark_light_controller()->SetDarkModeEnabledForTest(false);
+
+  MockPaletteObserver observer;
+  base::ScopedObservation<ColorPaletteController,
+                          ColorPaletteController::Observer>
+      observation(&observer);
+  observation.Observe(color_palette_controller());
+
+  EXPECT_CALL(observer, OnColorPaletteChanging(testing::Field(
+                            &ColorPaletteSeed::color_mode,
+                            ui::ColorProviderManager::ColorMode::kDark)))
+      .Times(1);
+  dark_light_controller()->SetDarkModeEnabledForTest(true);
+}
+
 }  // namespace ash
diff --git a/ash/style/dark_light_mode_controller_impl.cc b/ash/style/dark_light_mode_controller_impl.cc
index cb75246..e105f8f 100644
--- a/ash/style/dark_light_mode_controller_impl.cc
+++ b/ash/style/dark_light_mode_controller_impl.cc
@@ -294,8 +294,14 @@
 void DarkLightModeControllerImpl::NotifyColorModeChanges() {
   const bool is_enabled = IsDarkModeEnabled();
   cros_styles::SetDarkModeEnabled(is_enabled);
-  for (auto& observer : observers_)
+  if (last_value_ == is_enabled) {
+    // Updating the pref causes a notification. Skip it if it happens.
+    return;
+  }
+  last_value_ = is_enabled;
+  for (auto& observer : observers_) {
     observer.OnColorModeChanged(is_enabled);
+  }
 
   RefreshColorsOnColorMode(IsDarkModeEnabled());
 }
diff --git a/ash/style/dark_light_mode_controller_impl.h b/ash/style/dark_light_mode_controller_impl.h
index ed95350..788b14a 100644
--- a/ash/style/dark_light_mode_controller_impl.h
+++ b/ash/style/dark_light_mode_controller_impl.h
@@ -109,6 +109,10 @@
 
   OobeDialogState oobe_state_ = OobeDialogState::HIDDEN;
 
+  // Keep track of the last value that was sent to avoid multiple
+  // notifications.
+  absl::optional<bool> last_value_;
+
   // absl::nullopt in case no user pod is focused.
   absl::optional<bool> is_dark_mode_enabled_for_focused_pod_;
 
diff --git a/ash/system/audio/mic_gain_slider_controller.cc b/ash/system/audio/mic_gain_slider_controller.cc
index 0946618..e8be696 100644
--- a/ash/system/audio/mic_gain_slider_controller.cc
+++ b/ash/system/audio/mic_gain_slider_controller.cc
@@ -93,8 +93,9 @@
 
   TrackToggleUMA(/*target_toggle_state=*/mute);
 
-  audio_handler->SetMuteForDevice(audio_handler->GetPrimaryActiveInputNode(),
-                                  mute);
+  audio_handler->SetMuteForDevice(
+      audio_handler->GetPrimaryActiveInputNode(), mute,
+      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);
 }
 
 void MicGainSliderController::RecordGainChanged() {
diff --git a/ash/system/audio/mic_gain_slider_controller_unittest.cc b/ash/system/audio/mic_gain_slider_controller_unittest.cc
index 8f0d6ff..fbb26fc 100644
--- a/ash/system/audio/mic_gain_slider_controller_unittest.cc
+++ b/ash/system/audio/mic_gain_slider_controller_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "ash/system/audio/mic_gain_slider_controller.h"
 
+#include <memory>
+
 #include "ash/constants/ash_features.h"
 #include "ash/shell.h"
 #include "ash/system/audio/mic_gain_slider_view.h"
@@ -14,6 +16,7 @@
 #include "base/time/time.h"
 #include "chromeos/ash/components/audio/cras_audio_handler.h"
 #include "ui/views/controls/slider.h"
+#include "ui/views/widget/widget.h"
 
 namespace ash {
 
@@ -35,6 +38,20 @@
 
   ~MicGainSliderControllerTest() override = default;
 
+  void SetUp() override {
+    AshTestBase::SetUp();
+    widget_ = CreateFramelessTestWidget();
+    widget_->SetFullscreen(true);
+    slider_view_ = static_cast<MicGainSliderView*>(
+        mic_gain_slider_controller_.CreateView());
+    widget_->SetContentsView(slider_view_);
+  }
+
+  void TearDown() override {
+    widget_.reset();
+    AshTestBase::TearDown();
+  }
+
   bool IsQsRevampEnabled() const { return GetParam(); }
 
   views::View* GetMuteToastView() {
@@ -48,11 +65,15 @@
         /*old_value=*/0, views::SliderChangeReason::kByUser);
   }
 
+  void PressSliderButton() { LeftClickOn(slider_view_->button()); }
+
   base::HistogramTester histogram_tester_;
 
  private:
   base::test::ScopedFeatureList feature_list_;
   MicGainSliderController mic_gain_slider_controller_;
+  MicGainSliderView* slider_view_ = nullptr;
+  std::unique_ptr<views::Widget> widget_;
 };
 
 INSTANTIATE_TEST_SUITE_P(QsRevamp,
@@ -117,4 +138,12 @@
   delete toast_view;
 }
 
+// Verify pressing the mute button is recorded to metrics.
+TEST_P(MicGainSliderControllerTest, RecordInputGainMuteSource) {
+  PressSliderButton();
+  histogram_tester_.ExpectBucketCount(
+      CrasAudioHandler::kInputGainMuteSourceHistogramName,
+      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray, 1);
+}
+
 }  // namespace ash
diff --git a/ash/system/audio/unified_volume_slider_controller.cc b/ash/system/audio/unified_volume_slider_controller.cc
index 04031af..2a7a24f 100644
--- a/ash/system/audio/unified_volume_slider_controller.cc
+++ b/ash/system/audio/unified_volume_slider_controller.cc
@@ -101,7 +101,8 @@
 
   TrackToggleUMA(/*target_toggle_state=*/mute);
 
-  audio_handler->SetOutputMute(mute);
+  audio_handler->SetOutputMute(
+      mute, CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);
 }
 
 void UnifiedVolumeSliderController::RecordVolumeSourceMetric() {
diff --git a/ash/system/audio/unified_volume_slider_controller_unittest.cc b/ash/system/audio/unified_volume_slider_controller_unittest.cc
index 40b7ea6..ddccda47 100644
--- a/ash/system/audio/unified_volume_slider_controller_unittest.cc
+++ b/ash/system/audio/unified_volume_slider_controller_unittest.cc
@@ -4,16 +4,28 @@
 
 #include "ash/system/audio/unified_volume_slider_controller.h"
 
+#include <memory>
+
 #include "ash/shell.h"
+#include "ash/system/audio/unified_volume_view.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "chromeos/ash/components/audio/cras_audio_handler.h"
 #include "ui/views/controls/slider.h"
+#include "ui/views/widget/widget.h"
 
 namespace ash {
 
+class FakeDelegate : public UnifiedVolumeSliderController::Delegate {
+ public:
+  FakeDelegate() = default;
+  ~FakeDelegate() override = default;
+
+  void OnAudioSettingsButtonClicked() override {}
+};
+
 class UnifiedVolumeSliderControllerTest : public AshTestBase {
  public:
   UnifiedVolumeSliderControllerTest()
@@ -26,17 +38,40 @@
 
   ~UnifiedVolumeSliderControllerTest() override = default;
 
+  void SetUp() override {
+    AshTestBase::SetUp();
+    delegate_ = std::make_unique<FakeDelegate>();
+    unified_volume_slider_controller_ =
+        std::make_unique<UnifiedVolumeSliderController>(delegate_.get());
+    widget_ = CreateFramelessTestWidget();
+    widget_->SetFullscreen(true);
+    slider_view_ = static_cast<UnifiedVolumeView*>(
+        unified_volume_slider_controller_->CreateView());
+    widget_->SetContentsView(slider_view_);
+  }
+
+  void TearDown() override {
+    widget_.reset();
+    AshTestBase::TearDown();
+  }
+
  protected:
   void UpdateSliderValue(float new_value) {
-    unified_volume_slider_controller_.SliderValueChanged(
+    unified_volume_slider_controller_->SliderValueChanged(
         /*sender=*/nullptr, new_value,
         /*old_value=*/0, views::SliderChangeReason::kByUser);
   }
 
+  void PressSliderButton() { LeftClickOn(slider_view_->button()); }
+
   base::HistogramTester histogram_tester_;
 
  private:
-  UnifiedVolumeSliderController unified_volume_slider_controller_;
+  std::unique_ptr<UnifiedVolumeSliderController>
+      unified_volume_slider_controller_;
+  std::unique_ptr<FakeDelegate> delegate_;
+  UnifiedVolumeView* slider_view_ = nullptr;
+  std::unique_ptr<views::Widget> widget_;
 };
 
 // Verify moving the slider and changing the output volume is recorded to
@@ -78,4 +113,12 @@
       CrasAudioHandler::AudioSettingsChangeSource::kSystemTray, 2);
 }
 
+// Verify pressing the mute button is recorded to metrics.
+TEST_F(UnifiedVolumeSliderControllerTest, RecordOuptputVolumeMuteSource) {
+  PressSliderButton();
+  histogram_tester_.ExpectBucketCount(
+      CrasAudioHandler::kOutputVolumeMuteSourceHistogramName,
+      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray, 1);
+}
+
 }  // namespace ash
diff --git a/ash/system/time/calendar_view.cc b/ash/system/time/calendar_view.cc
index 97f7d31..513a1ee 100644
--- a/ash/system/time/calendar_view.cc
+++ b/ash/system/time/calendar_view.cc
@@ -62,7 +62,6 @@
     kContentHorizontalPadding - calendar_utils::kDateHorizontalPadding;
 constexpr int kExpandedCalendarPadding = 11;
 constexpr int kChevronPadding = calendar_utils::kColumnSetPadding - 1;
-constexpr int kEventListViewVerticalPadding = 6;
 constexpr int kMonthHeaderLabelTopPadding = 14;
 constexpr int kMonthHeaderLabelBottomPadding = 2;
 constexpr int kEventListViewHorizontalOffset = 1;
@@ -2042,9 +2041,9 @@
   const int x_position = scroll_view_->x() + kEventListViewHorizontalOffset;
   const int width = scroll_view_->GetVisibleRect().width() -
                     kEventListViewHorizontalOffset * 2;
-  const int event_list_view_height =
-      GetBoundsInScreen().bottom() - scroll_view_->GetBoundsInScreen().y() -
-      calendar_view_controller_->row_height() + kEventListViewVerticalPadding;
+  const int event_list_view_height = GetBoundsInScreen().bottom() -
+                                     scroll_view_->GetBoundsInScreen().y() -
+                                     calendar_view_controller_->row_height();
 
   // If the event list view is showing, position the calendar sliding surface
   // where the opened event list view will be.
diff --git a/ash/webui/personalization_app/resources/BUILD.gn b/ash/webui/personalization_app/resources/BUILD.gn
index 9fc3bfb..1b16a1d 100644
--- a/ash/webui/personalization_app/resources/BUILD.gn
+++ b/ash/webui/personalization_app/resources/BUILD.gn
@@ -169,5 +169,17 @@
     optimize_webui_in_files = [ "js/personalization_app.js" ]
   }
 
+  # Time of day assets cannot be made public until after the feature is
+  # released. Thus, they are currently hosted in CIPD and downloaded to
+  # this directory only if an internal chrome-branded checkout is being used.
+  # TODO(b/275379819): Create a custom GN arg like "tod_screensaver_enabled" and set it based
+  # on "feature_management" use flag in chromeos-chrome.ebuild.
+  if (is_chrome_branded) {
+    static_files += [
+      "time_of_day/thumbnails/clouds.jpg",
+      "time_of_day/thumbnails/new_mexico.jpg",
+    ]
+  }
+
   grit_output_dir = "$root_gen_dir/ash/webui"
 }
diff --git a/ash/webui/personalization_app/resources/README b/ash/webui/personalization_app/resources/README
new file mode 100644
index 0000000..16568c9
--- /dev/null
+++ b/ash/webui/personalization_app/resources/README
@@ -0,0 +1,15 @@
+The time-of-day resources cannot be made public until after the feature has been
+launched. Thus, they are currently hosted in CIPD and downloaded to this
+directory only if an internal chrome-branded checkout is being used. To update
+the assets in cipd:
+* `cd` to this directory in your local checkout
+* Update the time_of_day/ directory locally with desired changes.
+* Rebuild and test it.
+* `cipd auth-login`
+* `cipd create -pkg-def=cipd_time_of_day.yaml`. Outputs something like this:
+
+Instance: chromeos_internal/assistant/time_of_day:<version_id>
+ • Instance chromeos_internal/assistant/time_of_day:<version_id> was successfully registered
+
+* Open chromium/src/DEPS and find "src/ash/webui/personalization_app/resources".
+  Update the "version" field to the <version_id> printed above.
diff --git a/ash/webui/personalization_app/resources/cipd_time_of_day.yaml b/ash/webui/personalization_app/resources/cipd_time_of_day.yaml
new file mode 100644
index 0000000..16129bcb
--- /dev/null
+++ b/ash/webui/personalization_app/resources/cipd_time_of_day.yaml
@@ -0,0 +1,4 @@
+package: chromeos_internal/assistant/time_of_day
+description: Resources for time-of-day wallpaper and screensaver.
+data:
+  - dir: time_of_day
diff --git a/ash/webui/projector_app/BUILD.gn b/ash/webui/projector_app/BUILD.gn
index 7b2aa84..52df83d 100644
--- a/ash/webui/projector_app/BUILD.gn
+++ b/ash/webui/projector_app/BUILD.gn
@@ -10,8 +10,6 @@
 
 static_library("projector_app") {
   sources = [
-    "annotator_page_handler_impl.cc",
-    "annotator_page_handler_impl.h",
     "projector_app_client.cc",
     "projector_app_client.h",
     "projector_message_handler.cc",
@@ -22,10 +20,10 @@
     "projector_screencast.h",
     "projector_xhr_sender.cc",
     "projector_xhr_sender.h",
-    "trusted_projector_annotator_ui.cc",
-    "trusted_projector_annotator_ui.h",
     "trusted_projector_ui.cc",
     "trusted_projector_ui.h",
+    "untrusted_annotator_page_handler_impl.cc",
+    "untrusted_annotator_page_handler_impl.h",
     "untrusted_projector_annotator_ui.cc",
     "untrusted_projector_annotator_ui.h",
     "untrusted_projector_ui.cc",
@@ -39,7 +37,6 @@
     "//ash/webui/projector_app/mojom:annotator_mojo_bindings",
     "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings",
     "//ash/webui/resources:media_app_bundle_resources",
-    "//ash/webui/resources:projector_annotator_trusted_resources",
     "//ash/webui/resources:projector_annotator_untrusted_resources",
     "//ash/webui/resources:projector_app_bundle_resources",
     "//ash/webui/resources:projector_app_trusted_resources",
@@ -82,10 +79,10 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "test/annotator_page_handler_impl_unittest.cc",
     "test/projector_message_handler_unittest.cc",
     "test/projector_oauth_token_fetcher_unittest.cc",
     "test/projector_xhr_sender_unittest.cc",
+    "test/untrusted_annotator_page_handler_impl_unittest.cc",
   ]
 
   deps = [
diff --git a/ash/webui/projector_app/annotator_page_handler_impl.h b/ash/webui/projector_app/annotator_page_handler_impl.h
deleted file mode 100644
index 8808cef0..0000000
--- a/ash/webui/projector_app/annotator_page_handler_impl.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WEBUI_PROJECTOR_APP_ANNOTATOR_PAGE_HANDLER_IMPL_H_
-#define ASH_WEBUI_PROJECTOR_APP_ANNOTATOR_PAGE_HANDLER_IMPL_H_
-
-#include "ash/public/cpp/projector/projector_annotator_controller.h"
-#include "ash/webui/projector_app/mojom/annotator.mojom.h"
-#include "base/functional/callback.h"
-#include "base/values.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace content {
-class WebUI;
-}  // namespace content
-
-namespace ash {
-
-struct AnnotatorTool;
-
-// Handles communication with the Annotator WebUI (i.e.
-// chrome://projector/annotator/annotator_embedder.html).
-class AnnotatorPageHandlerImpl : public annotator::mojom::AnnotatorPageHandler {
- public:
-  AnnotatorPageHandlerImpl(
-      mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandler>
-          annotator_handler,
-      mojo::PendingRemote<annotator::mojom::AnnotatorPage> annotator,
-      content::WebUI* web_ui);
-  AnnotatorPageHandlerImpl(const AnnotatorPageHandlerImpl&) = delete;
-  AnnotatorPageHandlerImpl& operator=(const AnnotatorPageHandlerImpl&) = delete;
-  ~AnnotatorPageHandlerImpl() override;
-
-  // Called by ProjectorAppClient.
-  void SetTool(const AnnotatorTool& tool);
-  void Undo();
-  void Redo();
-  void Clear();
-
-  // annotator::mojom::AnnotatorHandler:
-  void OnUndoRedoAvailabilityChanged(bool undo_available,
-                                     bool redo_available) override;
-  void OnCanvasInitialized(bool success) override;
-  void OnError(const std::vector<std::string>& messages) override;
-
-  content::WebUI* get_web_ui_for_test() { return web_ui_; }
-
- private:
-  mojo::Remote<annotator::mojom::AnnotatorPage> annotator_remote_;
-  mojo::Receiver<annotator::mojom::AnnotatorPageHandler>
-      annotator_handler_receiver_;
-
-  // The WebUI that owns the TrustedProjectorAnnotatorUI that owns this
-  // instance.
-  content::WebUI* const web_ui_;
-};
-
-}  // namespace ash
-
-#endif  // ASH_WEBUI_PROJECTOR_APP_ANNOTATOR_PAGE_HANDLER_IMPL_H_
diff --git a/ash/webui/projector_app/mojom/BUILD.gn b/ash/webui/projector_app/mojom/BUILD.gn
index 95890cd..2ee3298 100644
--- a/ash/webui/projector_app/mojom/BUILD.gn
+++ b/ash/webui/projector_app/mojom/BUILD.gn
@@ -10,7 +10,7 @@
 cur_dir = rebase_path(".", "//")
 
 mojom("annotator_mojo_bindings") {
-  sources = [ "annotator.mojom" ]
+  sources = [ "untrusted_annotator.mojom" ]
   deps = [ "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings" ]
   webui_module_path = "/$cur_dir"
 }
diff --git a/ash/webui/projector_app/mojom/annotator.mojom b/ash/webui/projector_app/mojom/untrusted_annotator.mojom
similarity index 78%
rename from ash/webui/projector_app/mojom/annotator.mojom
rename to ash/webui/projector_app/mojom/untrusted_annotator.mojom
index fca08dc..2059657c 100644
--- a/ash/webui/projector_app/mojom/annotator.mojom
+++ b/ash/webui/projector_app/mojom/untrusted_annotator.mojom
@@ -8,7 +8,7 @@
 
 // AnnotatorPage interface implemented in the Javascript. Used by the browser
 // process to send events to Javascript.
-interface AnnotatorPage {
+interface UntrustedAnnotatorPage {
     // Clears the annotations on the annotator canvas.
     Clear();
     // Undo the last stroke on the annotator canvas.
@@ -21,7 +21,7 @@
 
 // AnnotatoPagerHandler interface implemented in C++ in the browser process.
 // Used by Javascript to communicate with the browser process.
-interface AnnotatorPageHandler {
+interface UntrustedAnnotatorPageHandler {
    // Notifies the browser that that undo/redo availability
    // changed for annotator.
    OnUndoRedoAvailabilityChanged(bool undo_available,
@@ -30,17 +30,13 @@
   // Notifies the browser process that the annotation canvas
   // has been initialized.
   OnCanvasInitialized(bool success);
-
-  // Notifies the browser process that an error has occurred
-  // in the renderer and sends the error messages to it.
-  OnError(array<string> errors);
 };
 
 // Interface used to setup the communication between Javascript and C++
 // in the browser process.
-interface AnnotatorPageHandlerFactory {
+interface UntrustedAnnotatorPageHandlerFactory {
   // Creates the AnnotatorPageHandler in the browser process and binds it
   // to receive calls from Javascript.
-  Create(pending_receiver<AnnotatorPageHandler> handler,
-         pending_remote<AnnotatorPage> annotator);
+  Create(pending_receiver<UntrustedAnnotatorPageHandler> handler,
+         pending_remote<UntrustedAnnotatorPage> annotator);
 };
diff --git a/ash/webui/projector_app/projector_app_client.h b/ash/webui/projector_app/projector_app_client.h
index f59e2dc..96a51d0b 100644
--- a/ash/webui/projector_app/projector_app_client.h
+++ b/ash/webui/projector_app/projector_app_client.h
@@ -25,7 +25,7 @@
 
 namespace ash {
 
-class AnnotatorPageHandlerImpl;
+class UntrustedAnnotatorPageHandlerImpl;
 struct AnnotatorTool;
 struct ProjectorScreencastVideo;
 struct NewScreencastPrecondition;
@@ -160,11 +160,13 @@
 
   // Registers the AnnotatorPageHandlerImpl that is owned by the WebUI that
   // contains the Projector annotator.
-  virtual void SetAnnotatorPageHandler(AnnotatorPageHandlerImpl* handler) = 0;
+  virtual void SetAnnotatorPageHandler(
+      UntrustedAnnotatorPageHandlerImpl* handler) = 0;
 
   // Resets the stored AnnotatorPageHandlerImpl if it matches the one that is
   // passed in.
-  virtual void ResetAnnotatorPageHandler(AnnotatorPageHandlerImpl* handler) = 0;
+  virtual void ResetAnnotatorPageHandler(
+      UntrustedAnnotatorPageHandlerImpl* handler) = 0;
 
   // Sets the tool inside the annotator WebUI.
   virtual void SetTool(const AnnotatorTool& tool) = 0;
diff --git a/ash/webui/projector_app/public/cpp/projector_app_constants.cc b/ash/webui/projector_app/public/cpp/projector_app_constants.cc
index 8fa9675..eaaa2443 100644
--- a/ash/webui/projector_app/public/cpp/projector_app_constants.cc
+++ b/ash/webui/projector_app/public/cpp/projector_app_constants.cc
@@ -15,7 +15,6 @@
     "https://screencast.apps.chrome";
 
 const char kChromeUITrustedProjectorUrl[] = "chrome://projector/";
-const char kChromeUITrustedAnnotatorUrl[] = "chrome://projector-annotator/";
 const char kChromeUIUntrustedAnnotatorUrl[] =
     "chrome-untrusted://projector-annotator/";
 
diff --git a/ash/webui/projector_app/public/cpp/projector_app_constants.h b/ash/webui/projector_app/public/cpp/projector_app_constants.h
index ee43ff1..8108b594 100644
--- a/ash/webui/projector_app/public/cpp/projector_app_constants.h
+++ b/ash/webui/projector_app/public/cpp/projector_app_constants.h
@@ -16,7 +16,6 @@
 extern const char kChromeUIUntrustedProjectorPwaUrl[];
 
 extern const char kChromeUITrustedProjectorUrl[];
-extern const char kChromeUITrustedAnnotatorUrl[];
 extern const char kChromeUIUntrustedAnnotatorUrl[];
 
 extern const char kChromeUITrustedProjectorSwaAppId[];
diff --git a/ash/webui/projector_app/resources/BUILD.gn b/ash/webui/projector_app/resources/BUILD.gn
index 0cae46dc..989adb1d 100644
--- a/ash/webui/projector_app/resources/BUILD.gn
+++ b/ash/webui/projector_app/resources/BUILD.gn
@@ -11,7 +11,6 @@
 js_type_check("closure_compile") {
   is_polymer3 = true
   deps = [
-    "//ash/webui/projector_app/resources/annotator/trusted:trusted_annotator_library",
     "//ash/webui/projector_app/resources/annotator/untrusted:untrusted_annotator_library",
     "//ash/webui/projector_app/resources/app/trusted:trusted_app",
     "//ash/webui/projector_app/resources/app/untrusted:untrusted_app",
diff --git a/ash/webui/projector_app/resources/annotator/trusted/BUILD.gn b/ash/webui/projector_app/resources/annotator/trusted/BUILD.gn
deleted file mode 100644
index 88d8fbd..0000000
--- a/ash/webui/projector_app/resources/annotator/trusted/BUILD.gn
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright 2022 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//chrome/common/features.gni")
-import("//third_party/closure_compiler/compile_js.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
-
-assert(is_chromeos_ash, "Projector Annotator is ChromeOS only")
-
-js_library("trusted_annotator_library") {
-  sources = [
-    "annotator_browser_proxy.js",
-    "annotator_embedder_impl.js",
-    "trusted_annotator_comm_factory.js",
-  ]
-  deps = [
-    "//ash/webui/common/resources:cr_deprecated",
-    "//ash/webui/common/resources/post_message_api:post_message_api_client",
-    "//ash/webui/common/resources/post_message_api:post_message_api_request_handler",
-    "//ash/webui/projector_app/mojom:annotator_mojo_bindings_webui_js",
-    "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings_webui_js",
-    "//ash/webui/projector_app/resources/common:message_types",
-  ]
-  externs_list = [ "../../common/projector_app.externs.js" ]
-}
-
-generate_grd("build_trusted_grd") {
-  input_files = [
-    "annotator_embedder.html",
-    "annotator_embedder.css",
-    "annotator_browser_proxy.js",
-    "trusted_annotator_comm_factory.js",
-    "annotator_embedder_impl.js",
-  ]
-
-  manifest_files = []
-  input_files_base_dir = rebase_path(".", "//")
-  grd_prefix = "ash_projector_annotator_trusted"
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
-  deps = [
-    "//ash/webui/projector_app/mojom:annotator_mojo_bindings_webui_grdp",
-    "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings_webui_grdp",
-  ]
-  grdp_files = [
-    "$target_gen_dir/../../../mojom/annotator_mojo_bindings_webui_resources.grdp",
-    "$target_gen_dir/../../../public/mojom/annotator_mojo_bindings_webui_resources.grdp",
-  ]
-}
diff --git a/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.css b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.css
deleted file mode 100644
index 0cd16f1..0000000
--- a/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.css
+++ /dev/null
@@ -1,19 +0,0 @@
-/* Copyright 2021 The Chromium Authors
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-body {
-  background-color: transparent;
-  height: 100vh;
-  margin: 0;
-  overflow: hidden;
-  width: 100vw;
-}
-
-.marker-iframe {
-  border: none;
-  height: 100vh;
-  margin: 0;
-  overflow: hidden;
-  width: 100vw;
-}
diff --git a/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.html b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.html
deleted file mode 100644
index 105c6d3..0000000
--- a/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<!DOCTYPE html>
-<html>
-<head>
-    <link rel="stylesheet" href="annotator_embedder.css">
-    <script type="module" src="annotator_embedder_impl.js"></script>
-  </head>
-  <body>
-    <iframe
-      class="marker-iframe"
-      src="chrome-untrusted://projector-annotator"
-      allow="cross-origin-isolated">
-    </iframe>
-  </body>
-</html>
diff --git a/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder_impl.js b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder_impl.js
deleted file mode 100644
index 97652a40..0000000
--- a/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder_impl.js
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2021 The Chromium Authors.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {AnnotatorBrowserProxyImpl} from './annotator_browser_proxy.js';
-import {AnnotatorPageCallbackRouter} from './ash/webui/projector_app/mojom/annotator.mojom-webui.js';
-import {AnnotatorTrustedCommFactory} from './trusted_annotator_comm_factory.js';
-
-/**
- * Enum for passing annotator error message to the browser process.
- * @enum {string}
- */
-const AnnotatorToolErrorType = {
-  UNDO_ERROR: 'UNDO_ERROR',
-  REDO_ERROR: 'REDO_ERROR',
-  CLEAR_ERROR: 'CLEAR_ERROR',
-  SET_TOOL_ERROR: 'SET_TOOL_ERROR',
-};
-
-
-/* @type {UntrustedAnnotatorClient} */
-const client = AnnotatorTrustedCommFactory.getPostMessageAPIClient();
-
-/* @type {AnnotatorPageCallbackRouter} */
-const annotatorPageRouter =
-    AnnotatorBrowserProxyImpl.getInstance().getAnnotatorCallbackRouter();
-
-annotatorPageRouter.undo.addListener(() => {
-  try {
-    client.undo();
-  } catch (error) {
-    AnnotatorBrowserProxyImpl.getInstance().onError(
-        [AnnotatorToolErrorType.UNDO_ERROR]);
-  }
-});
-
-annotatorPageRouter.redo.addListener(() => {
-  try {
-    client.redo();
-  } catch (error) {
-    AnnotatorBrowserProxyImpl.getInstance().onError(
-        [AnnotatorToolErrorType.REDO_ERROR]);
-  }
-});
-
-
-annotatorPageRouter.clear.addListener(() => {
-  try {
-    client.clear();
-  } catch (error) {
-    AnnotatorBrowserProxyImpl.getInstance().onError(
-        [AnnotatorToolErrorType.CLEAR_ERROR]);
-  }
-});
-
-
-annotatorPageRouter.setTool.addListener((tool) => {
-  try {
-    client.setTool(tool);
-  } catch (error) {
-    AnnotatorBrowserProxyImpl.getInstance().onError(
-        [AnnotatorToolErrorType.SET_TOOL_ERROR]);
-  }
-});
\ No newline at end of file
diff --git a/ash/webui/projector_app/resources/annotator/trusted/trusted_annotator_comm_factory.js b/ash/webui/projector_app/resources/annotator/trusted/trusted_annotator_comm_factory.js
deleted file mode 100644
index bb2bcb4..0000000
--- a/ash/webui/projector_app/resources/annotator/trusted/trusted_annotator_comm_factory.js
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {PostMessageAPIClient} from 'chrome://resources/ash/common/post_message_api/post_message_api_client.js';
-import {RequestHandler} from 'chrome://resources/ash/common/post_message_api/post_message_api_request_handler.js';
-
-import {AnnotatorBrowserProxy, AnnotatorBrowserProxyImpl} from './annotator_browser_proxy.js';
-
-const TARGET_URL = 'chrome-untrusted://projector-annotator/';
-
-// A PostMessageAPIClient that sends messages to chrome-untrusted://projector.
-export class UntrustedAnnotatorClient extends PostMessageAPIClient {
-  /**
-   * @param {!Window} targetWindow
-   */
-  constructor(targetWindow) {
-    super(TARGET_URL, targetWindow);
-  }
-
-  /**
-   * Notifies the Annotator tool to update the tool.
-   * @param {!projectorApp.AnnotatorToolParams} tool
-   * @return {Promise<boolean>}
-   */
-  setTool(tool) {
-    return this.callApiFn('setTool', [tool]);
-  }
-
-  /**
-   * Notifies the Annotator to undo the last stroke.
-   * @return {Promise<boolean>}
-   */
-  undo() {
-    return this.callApiFn('undo', []);
-  }
-
-  /**
-   * Notifies the Annotator to redo the last stroke.
-   * @return {Promise<boolean>}
-   */
-  redo() {
-    return this.callApiFn('redo', []);
-  }
-
-  /**
-   * Notifies the Annotator to clear the screen.
-   * @return {Promise<boolean>}
-   */
-  clear() {
-    return this.callApiFn('clear', []);
-  }
-}
-
-/**
- * Class that implements the RequestHandler inside the Projector trusted scheme
- * for Annotator.
- */
-class TrustedAnnotatorRequestHandler extends RequestHandler {
-  /*
-   * @param {!Element} iframeElement The <iframe> element to listen to as a
-   *     client.
-   * @param {AnnotatorBrowserProxy} browserProxy The browser proxy that will
-   *     be used to handle the messages.
-   */
-  constructor(iframeElement, browserProxy) {
-    super(iframeElement, TARGET_URL, TARGET_URL);
-    this.browserProxy_ = browserProxy;
-
-    this.registerMethod('onUndoRedoAvailabilityChanged', (values) => {
-      if (!values || values.length != 2) {
-        return;
-      }
-      return this.browserProxy_.onUndoRedoAvailabilityChanged(
-          values[0], values[1]);
-    });
-
-    this.registerMethod('onCanvasInitialized', (values) => {
-      if (!values || values.length != 1) {
-        return;
-      }
-      return this.browserProxy_.onCanvasInitialized(values[0]);
-    });
-  }
-}
-
-/**
- * This is a class that is used to setup the duplex communication
- * channels between this origin, chrome://projector/* and the iframe embedded
- * inside the document.
- */
-export class AnnotatorTrustedCommFactory {
-  /**
-   * Creates the instances of PostMessageAPIClient and RequestHandler if they
-   * have not been created already.
-   */
-  static maybeCreateInstances() {
-    if (AnnotatorTrustedCommFactory.client_ ||
-        AnnotatorTrustedCommFactory.requestHandler_) {
-      return;
-    }
-
-    const iframeElement = document.getElementsByTagName('iframe')[0];
-
-    AnnotatorTrustedCommFactory.client_ =
-        new UntrustedAnnotatorClient(iframeElement.contentWindow);
-
-    AnnotatorTrustedCommFactory.requestHandler_ =
-        new TrustedAnnotatorRequestHandler(
-            iframeElement, AnnotatorBrowserProxyImpl.getInstance());
-  }
-
-  /**
-   * In order to use this class, please do the following
-   * (e.g. to set the tool do the following):
-   * const success = await
-   * AnnotatorTrustedCommFactory.getPostMessageAPIClient().setTool(tool);
-   *
-   * @return {!UntrustedAnnotatorClient}
-   */
-  static getPostMessageAPIClient() {
-    // AnnotatorTrustedCommFactory.client_ should be available. However to be on
-    // the cautious side create an instance here if getPostMessageAPIClient is
-    // triggered before the page finishes loading.
-    AnnotatorTrustedCommFactory.maybeCreateInstances();
-    return AnnotatorTrustedCommFactory.client_;
-  }
-}
-
-document.addEventListener('DOMContentLoaded', () => {
-  // Create instances of the singletons(PostMessageAPIClient and
-  // RequestHandler) when the document has finished loading.
-  AnnotatorTrustedCommFactory.maybeCreateInstances();
-});
diff --git a/ash/webui/projector_app/resources/annotator/untrusted/BUILD.gn b/ash/webui/projector_app/resources/annotator/untrusted/BUILD.gn
index 088d9cd0e..53efe48f 100644
--- a/ash/webui/projector_app/resources/annotator/untrusted/BUILD.gn
+++ b/ash/webui/projector_app/resources/annotator/untrusted/BUILD.gn
@@ -9,10 +9,13 @@
 assert(is_chromeos_ash, "Projector Annotator is ChromeOS only")
 
 js_library("untrusted_annotator_library") {
-  sources = [ "untrusted_annotator_comm_factory.js" ]
+  sources = [
+    "annotator_browser_proxy.js",
+    "untrusted_annotator_comm_factory.js",
+  ]
   deps = [
-    "//ash/webui/common/resources/post_message_api:post_message_api_client",
-    "//ash/webui/common/resources/post_message_api:post_message_api_request_handler",
+    "//ash/webui/projector_app/mojom:annotator_mojo_bindings_webui_js",
+    "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings_webui_js",
     "//ash/webui/projector_app/resources/common:message_types",
   ]
   externs_list = [ "../../common/projector_app.externs.js" ]
@@ -20,6 +23,7 @@
 
 generate_grd("build_untrusted_grd") {
   input_files = [
+    "annotator_browser_proxy.js",
     "annotator.html",
     "untrusted_annotator_comm_factory.js",
   ]
@@ -27,4 +31,13 @@
   input_files_base_dir = rebase_path(".", "//")
   grd_prefix = "ash_projector_annotator_untrusted"
   out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+
+  deps = [
+    "//ash/webui/projector_app/mojom:annotator_mojo_bindings_webui_grdp",
+    "//ash/webui/projector_app/public/mojom:annotator_mojo_bindings_webui_grdp",
+  ]
+  grdp_files = [
+    "$target_gen_dir/../../../mojom/annotator_mojo_bindings_webui_resources.grdp",
+    "$target_gen_dir/../../../public/mojom/annotator_mojo_bindings_webui_resources.grdp",
+  ]
 }
diff --git a/ash/webui/projector_app/resources/annotator/trusted/annotator_browser_proxy.js b/ash/webui/projector_app/resources/annotator/untrusted/annotator_browser_proxy.js
similarity index 62%
rename from ash/webui/projector_app/resources/annotator/trusted/annotator_browser_proxy.js
rename to ash/webui/projector_app/resources/annotator/untrusted/annotator_browser_proxy.js
index 322941c5..11310906 100644
--- a/ash/webui/projector_app/resources/annotator/trusted/annotator_browser_proxy.js
+++ b/ash/webui/projector_app/resources/annotator/untrusted/annotator_browser_proxy.js
@@ -1,9 +1,8 @@
 // Copyright 2022 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-import {addSingletonGetter} from 'chrome://resources/ash/common/cr_deprecated.js';
 
-import {AnnotatorPageCallbackRouter, AnnotatorPageHandlerFactory, AnnotatorPageHandlerRemote, AnnotatorPageRemote} from './ash/webui/projector_app/mojom/annotator.mojom-webui.js';
+import {UntrustedAnnotatorPageCallbackRouter, UntrustedAnnotatorPageHandlerFactory, UntrustedAnnotatorPageHandlerRemote, UntrustedAnnotatorPageRemote} from './ash/webui/projector_app/mojom/untrusted_annotator.mojom-webui.js';
 
 /**
  * To use the annotator proxy, please import this module and call
@@ -28,14 +27,6 @@
    * @param {boolean} success
    */
   onCanvasInitialized(success) {}
-
-  /**
-   * Sends 'error' message to handler.
-   * The Handler will log the message. If the error is not a recoverable error,
-   * the handler closes the corresponding WebUI.
-   * @param {!Array<string>} msg Error messages.
-   */
-  onError(msg) {}
 }
 
 /**
@@ -43,9 +34,9 @@
  */
 export class AnnotatorBrowserProxyImpl {
   constructor() {
-    this.pageHandlerFactory = AnnotatorPageHandlerFactory.getRemote();
-    this.pageHandlerRemote = new AnnotatorPageHandlerRemote();
-    this.annotatorCallbackRouter = new AnnotatorPageCallbackRouter();
+    this.pageHandlerFactory = UntrustedAnnotatorPageHandlerFactory.getRemote();
+    this.pageHandlerRemote = new UntrustedAnnotatorPageHandlerRemote();
+    this.annotatorCallbackRouter = new UntrustedAnnotatorPageCallbackRouter();
 
     this.pageHandlerFactory.create(
         this.pageHandlerRemote.$.bindNewPipeAndPassReceiver(),
@@ -66,11 +57,9 @@
   onCanvasInitialized(success) {
     this.pageHandlerRemote.onCanvasInitialized(success);
   }
-
-  /** @override */
-  onError(msgs) {
-    this.pageHandlerRemote.onError(msgs);
-  }
 }
 
-addSingletonGetter(AnnotatorBrowserProxyImpl);
\ No newline at end of file
+/**
+ * @type {AnnotatorBrowserProxyImpl}
+ */
+export const browserProxy = new AnnotatorBrowserProxyImpl();
diff --git a/ash/webui/projector_app/resources/annotator/untrusted/untrusted_annotator_comm_factory.js b/ash/webui/projector_app/resources/annotator/untrusted/untrusted_annotator_comm_factory.js
index 8775c9d..f780a39 100644
--- a/ash/webui/projector_app/resources/annotator/untrusted/untrusted_annotator_comm_factory.js
+++ b/ash/webui/projector_app/resources/annotator/untrusted/untrusted_annotator_comm_factory.js
@@ -2,10 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PostMessageAPIClient} from '//resources/ash/common/post_message_api/post_message_api_client.js';
-import {RequestHandler} from '//resources/ash/common/post_message_api/post_message_api_request_handler.js';
-
-const TARGET_URL = 'chrome://projector-annotator/';
+import {AnnotatorBrowserProxyImpl, browserProxy} from './annotator_browser_proxy.js';
+import {UntrustedAnnotatorPageCallbackRouter} from './ash/webui/projector_app/mojom/untrusted_annotator.mojom-webui.js';
 
 /**
  * Returns the projector app element inside this current DOM.
@@ -16,129 +14,56 @@
       document.querySelector('projector-ink-canvas-wrapper'));
 }
 
-
-// A client that sends messages to the chrome://projector embedder.
-export class TrustedAnnotatorClient extends PostMessageAPIClient {
-  /**
-   * @param {!Window} parentWindow The embedder window from which requests
-   *     come.
-   */
-  constructor(parentWindow) {
-    super(TARGET_URL, parentWindow);
-  }
-
-  /**
-   * Notifies the native ui that undo/redo has become available.
-   * @param {boolean} undoAvailable
-   * @param {boolean} redoAvailable
-   * @return {Promise}
-   */
-  onUndoRedoAvailabilityChanged(undoAvailable, redoAvailable) {
-    return this.callApiFn(
-        'onUndoRedoAvailabilityChanged', [undoAvailable, redoAvailable]);
-  }
-
-  /**
-   * Notifies the native UI that the canvas has initialized.
-   * @param {boolean} success
-   * @return {Promise}
-   */
-  onCanvasInitialized(success) {
-    return this.callApiFn('onCanvasInitialized', [success]);
-  }
-}
-
-/**
- * Class that implements the RequestHandler inside the Projector untrusted
- * scheme for Annotator.
- */
-export class UntrustedAnnotatorRequestHandler extends RequestHandler {
-  /**
-   * @param {!Window} parentWindow The embedder window from which requests
-   *     come.
-   */
-  constructor(parentWindow) {
-    super(null, TARGET_URL, TARGET_URL);
-    this.targetWindow_ = parentWindow;
-
-    this.registerMethod('setTool', (args) => {
-      getAnnotatorElement().setTool(args[0]);
-      return true;
-    });
-
-    this.registerMethod('undo', () => {
-      getAnnotatorElement().undo();
-      return true;
-    });
-
-    this.registerMethod('redo', () => {
-      getAnnotatorElement().redo();
-      return true;
-    });
-
-    this.registerMethod('clear', () => {
-      getAnnotatorElement().clear();
-      return true;
-    });
-  }
-
-  /** @override */
-  targetWindow() {
-    return this.targetWindow_;
-  }
-}
-
-/**
- * This is a class that is used to setup the duplex communication channels
- * between this origin, chrome-untrusted://projector/* and the embedder content.
- */
-export class AnnotatorUntrustedCommFactory {
-  /**
-   * Creates the instances of PostMessageAPIClient and Requesthandler.
-   */
-  static maybeCreateInstances() {
-    if (AnnotatorUntrustedCommFactory.client_ ||
-        AnnotatorUntrustedCommFactory.requestHandler_) {
-      return;
-    }
-
-    AnnotatorUntrustedCommFactory.client_ =
-        new TrustedAnnotatorClient(window.parent);
-
-    AnnotatorUntrustedCommFactory.requestHandler_ =
-        new UntrustedAnnotatorRequestHandler(window.parent);
-    const elem = getAnnotatorElement();
-    elem.addUndoRedoListener((undoAvailable, redoAvailable) => {
-      AnnotatorUntrustedCommFactory.client_.onUndoRedoAvailabilityChanged(
-          undoAvailable, redoAvailable);
-    });
-    elem.addCanvasInitializationCallback((success) => {
-      AnnotatorUntrustedCommFactory.client_.onCanvasInitialized(success);
-    });
-  }
-
-  /**
-   * In order to use this class, please do the following (e.g. To notify when
-   * undo-redo becomes available):
-   * AnnotatorUntrustedCommFactory.
-   *     getPostMessageAPIClient().
-   *     onUndoRedoAvailabilityChanged(true, true);
-   * @return {!TrustedAnnotatorClient}
-   */
-  static getPostMessageAPIClient() {
-    // AnnotatorUntrustedCommFactory.client_ should be available. However to be
-    // on the cautious side create an instance here if getPostMessageAPIClient
-    // is triggered before the page finishes loading.
-    AnnotatorUntrustedCommFactory.maybeCreateInstances();
-    return AnnotatorUntrustedCommFactory.client_;
-  }
-}
+/* @type {AnnotatorPageCallbackRouter} */
+let annotatorPageRouter = null;
 
 const observer = new MutationObserver(() => {
   if (getAnnotatorElement()) {
-    // Create instances of the singletons(PostMessageAPIClient and
-    // RequestHandler) when the annotator element has been added to DOM tree.
-    AnnotatorUntrustedCommFactory.maybeCreateInstances();
+    if (annotatorPageRouter) {
+      // We have already registered. Therefore, return early.
+      return;
+    }
+
+    annotatorPageRouter = browserProxy.getAnnotatorCallbackRouter();
+
+    // Register for callbacks from the browser process.
+    annotatorPageRouter.undo.addListener(() => {
+      try {
+        getAnnotatorElement().undo();
+      } catch (error) {
+        console.error('AnnotatorToolErrorType.UNDO_ERROR', error);
+      }
+    });
+    annotatorPageRouter.redo.addListener(() => {
+      try {
+        getAnnotatorElement().redo();
+      } catch (error) {
+        console.error('AnnotatorToolErrorType.REDO_ERROR', error);
+      }
+    });
+    annotatorPageRouter.clear.addListener(() => {
+      try {
+        getAnnotatorElement().clear();
+      } catch (error) {
+        console.error('AnnotatorToolErrorType.CLEAR_ERROR', error);
+      }
+    });
+    annotatorPageRouter.setTool.addListener((tool) => {
+      try {
+        getAnnotatorElement().setTool(tool);
+      } catch (error) {
+        console.error('AnnotatorToolErrorType.SET_TOOL_ERROR', error);
+      }
+    });
+
+    // Pass calls to the browser process.
+    const elem = getAnnotatorElement();
+    elem.addUndoRedoListener((undoAvailable, redoAvailable) => {
+      browserProxy.onUndoRedoAvailabilityChanged(undoAvailable, redoAvailable);
+    });
+    elem.addCanvasInitializationCallback((success) => {
+      browserProxy.onCanvasInitialized(success);
+    });
   }
 });
 
diff --git a/ash/webui/projector_app/resources/app/trusted/projector_browser_proxy.js b/ash/webui/projector_app/resources/app/trusted/projector_browser_proxy.js
index 600c9f3..05d6833 100644
--- a/ash/webui/projector_app/resources/app/trusted/projector_browser_proxy.js
+++ b/ash/webui/projector_app/resources/app/trusted/projector_browser_proxy.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import {sendWithPromise} from 'chrome://resources/ash/common/cr.m.js';
-import {addSingletonGetter} from 'chrome://resources/ash/common/cr_deprecated.js';
 
 /**
  * To use the browser proxy, please import this module and call
@@ -125,9 +124,24 @@
 }
 
 /**
+ * @type {ProjectorBrowserProxyImpl}
+ */
+let browserProxy;
+
+/**
  * @implements {ProjectorBrowserProxy}
  */
 export class ProjectorBrowserProxyImpl {
+  /**
+   * @returns {ProjectorBrowserProxyImpl}
+   */
+  static getInstance() {
+    if (!browserProxy) {
+      browserProxy = new ProjectorBrowserProxyImpl();
+    }
+    return browserProxy;
+  }
+
   /** @override */
   getAccounts() {
     return sendWithPromise('getAccounts');
@@ -202,5 +216,3 @@
     return sendWithPromise('getVideo', [videoFileId, resourceKey]);
   }
 }
-
-addSingletonGetter(ProjectorBrowserProxyImpl);
diff --git a/ash/webui/projector_app/test/mock_app_client.h b/ash/webui/projector_app/test/mock_app_client.h
index 1a6b57fb..f1ca1498 100644
--- a/ash/webui/projector_app/test/mock_app_client.h
+++ b/ash/webui/projector_app/test/mock_app_client.h
@@ -55,8 +55,10 @@
                      void(const std::string&,
                           const std::string&,
                           ProjectorAppClient::OnGetVideoCallback));
-  MOCK_METHOD1(SetAnnotatorPageHandler, void(AnnotatorPageHandlerImpl*));
-  MOCK_METHOD1(ResetAnnotatorPageHandler, void(AnnotatorPageHandlerImpl*));
+  MOCK_METHOD1(SetAnnotatorPageHandler,
+               void(UntrustedAnnotatorPageHandlerImpl*));
+  MOCK_METHOD1(ResetAnnotatorPageHandler,
+               void(UntrustedAnnotatorPageHandlerImpl*));
   MOCK_METHOD1(SetTool, void(const AnnotatorTool&));
   MOCK_METHOD0(Clear, void());
   MOCK_METHOD1(NotifyAppUIActive, void(bool active));
diff --git a/ash/webui/projector_app/test/annotator_page_handler_impl_unittest.cc b/ash/webui/projector_app/test/untrusted_annotator_page_handler_impl_unittest.cc
similarity index 65%
rename from ash/webui/projector_app/test/annotator_page_handler_impl_unittest.cc
rename to ash/webui/projector_app/test/untrusted_annotator_page_handler_impl_unittest.cc
index c0ecb750..b9aaf28 100644
--- a/ash/webui/projector_app/test/annotator_page_handler_impl_unittest.cc
+++ b/ash/webui/projector_app/test/untrusted_annotator_page_handler_impl_unittest.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/webui/projector_app/annotator_page_handler_impl.h"
+#include "ash/webui/projector_app/untrusted_annotator_page_handler_impl.h"
 
 #include "ash/public/cpp/projector/annotator_tool.h"
 #include "ash/public/cpp/test/mock_projector_controller.h"
-#include "ash/webui/projector_app/mojom/annotator.mojom.h"
+#include "ash/webui/projector_app/mojom/untrusted_annotator.mojom.h"
 #include "ash/webui/projector_app/public/mojom/annotator_structs.mojom.h"
 #include "ash/webui/projector_app/test/mock_app_client.h"
 #include "base/test/task_environment.h"
@@ -23,12 +23,14 @@
 namespace {
 
 // MOCK the annotator instance in the WebUI renderer.
-class MockAnnotatorPage : public annotator::mojom::AnnotatorPage {
+class MockUntrustedAnnotatorPage
+    : public annotator::mojom::UntrustedAnnotatorPage {
  public:
-  MockAnnotatorPage() = default;
-  MockAnnotatorPage(const MockAnnotatorPage&) = delete;
-  MockAnnotatorPage& operator=(const MockAnnotatorPage&) = delete;
-  ~MockAnnotatorPage() override = default;
+  MockUntrustedAnnotatorPage() = default;
+  MockUntrustedAnnotatorPage(const MockUntrustedAnnotatorPage&) = delete;
+  MockUntrustedAnnotatorPage& operator=(const MockUntrustedAnnotatorPage&) =
+      delete;
+  ~MockUntrustedAnnotatorPage() override = default;
 
   MOCK_METHOD0(Clear, void());
   MOCK_METHOD0(Undo, void());
@@ -47,32 +49,33 @@
     remote_->OnCanvasInitialized(success);
   }
 
-  mojo::Receiver<annotator::mojom::AnnotatorPage>& receiver() {
+  mojo::Receiver<annotator::mojom::UntrustedAnnotatorPage>& receiver() {
     return receiver_;
   }
-  mojo::Remote<annotator::mojom::AnnotatorPageHandler>& remote() {
+  mojo::Remote<annotator::mojom::UntrustedAnnotatorPageHandler>& remote() {
     return remote_;
   }
 
  private:
-  mojo::Receiver<annotator::mojom::AnnotatorPage> receiver_{this};
-  mojo::Remote<annotator::mojom::AnnotatorPageHandler> remote_;
+  mojo::Receiver<annotator::mojom::UntrustedAnnotatorPage> receiver_{this};
+  mojo::Remote<annotator::mojom::UntrustedAnnotatorPageHandler> remote_;
 };
 
 }  // namespace
 
-class AnnotatorPageHandlerImplTest : public testing::Test {
+class UntrustedAnnotatorPageHandlerImplTest : public testing::Test {
  public:
-  AnnotatorPageHandlerImplTest() = default;
-  AnnotatorPageHandlerImplTest(const AnnotatorPageHandlerImplTest&) = delete;
-  AnnotatorPageHandlerImplTest& operator=(const AnnotatorPageHandlerImplTest&) =
-      delete;
-  ~AnnotatorPageHandlerImplTest() override = default;
+  UntrustedAnnotatorPageHandlerImplTest() = default;
+  UntrustedAnnotatorPageHandlerImplTest(
+      const UntrustedAnnotatorPageHandlerImplTest&) = delete;
+  UntrustedAnnotatorPageHandlerImplTest& operator=(
+      const UntrustedAnnotatorPageHandlerImplTest&) = delete;
+  ~UntrustedAnnotatorPageHandlerImplTest() override = default;
 
   // testing::Test:
   void SetUp() override {
-    annotator_ = std::make_unique<MockAnnotatorPage>();
-    handler_ = std::make_unique<AnnotatorPageHandlerImpl>(
+    annotator_ = std::make_unique<MockUntrustedAnnotatorPage>();
+    handler_ = std::make_unique<UntrustedAnnotatorPageHandlerImpl>(
         annotator().remote().BindNewPipeAndPassReceiver(),
         annotator().receiver().BindNewPipeAndPassRemote(),
         /*web_ui=*/nullptr);
@@ -83,9 +86,9 @@
     handler_.reset();
   }
 
-  AnnotatorPageHandlerImpl& handler() { return *handler_; }
+  UntrustedAnnotatorPageHandlerImpl& handler() { return *handler_; }
   MockProjectorController& controller() { return controller_; }
-  MockAnnotatorPage& annotator() { return *annotator_; }
+  MockUntrustedAnnotatorPage& annotator() { return *annotator_; }
   base::test::SingleThreadTaskEnvironment& task_environment() {
     return task_environment_;
   }
@@ -93,13 +96,13 @@
  private:
   base::test::SingleThreadTaskEnvironment task_environment_;
 
-  std::unique_ptr<MockAnnotatorPage> annotator_;
-  std::unique_ptr<AnnotatorPageHandlerImpl> handler_;
+  std::unique_ptr<MockUntrustedAnnotatorPage> annotator_;
+  std::unique_ptr<UntrustedAnnotatorPageHandlerImpl> handler_;
   MockProjectorController controller_;
   MockAppClient client_;
 };
 
-TEST_F(AnnotatorPageHandlerImplTest, SetTool) {
+TEST_F(UntrustedAnnotatorPageHandlerImplTest, SetTool) {
   AnnotatorTool expected_tool;
   expected_tool.color = SkColorSetARGB(0xA1, 0xB2, 0xC3, 0xD4);
   expected_tool.size = 5;
@@ -115,25 +118,25 @@
   annotator().FlushReceiverForTesting();
 }
 
-TEST_F(AnnotatorPageHandlerImplTest, Undo) {
+TEST_F(UntrustedAnnotatorPageHandlerImplTest, Undo) {
   EXPECT_CALL(annotator(), Undo());
   handler().Undo();
   annotator().FlushReceiverForTesting();
 }
 
-TEST_F(AnnotatorPageHandlerImplTest, Redo) {
+TEST_F(UntrustedAnnotatorPageHandlerImplTest, Redo) {
   EXPECT_CALL(annotator(), Redo());
   handler().Redo();
   annotator().FlushReceiverForTesting();
 }
 
-TEST_F(AnnotatorPageHandlerImplTest, Clear) {
+TEST_F(UntrustedAnnotatorPageHandlerImplTest, Clear) {
   EXPECT_CALL(annotator(), Clear());
   handler().Clear();
   annotator().FlushReceiverForTesting();
 }
 
-TEST_F(AnnotatorPageHandlerImplTest, UndoRedoAvailabilityChanged) {
+TEST_F(UntrustedAnnotatorPageHandlerImplTest, UndoRedoAvailabilityChanged) {
   EXPECT_CALL(controller(), OnUndoRedoAvailabilityChanged(false, false));
   annotator().SendUndoRedoAvailableChanged(false, false);
 
@@ -145,7 +148,7 @@
   annotator().FlushRemoteForTesting();
 }
 
-TEST_F(AnnotatorPageHandlerImplTest, CanvasInitialized) {
+TEST_F(UntrustedAnnotatorPageHandlerImplTest, CanvasInitialized) {
   EXPECT_CALL(controller(), OnCanvasInitialized(true));
   annotator().SendCanvasInitialized(true);
 
diff --git a/ash/webui/projector_app/trusted_projector_annotator_ui.cc b/ash/webui/projector_app/trusted_projector_annotator_ui.cc
deleted file mode 100644
index d8c2b3e9..0000000
--- a/ash/webui/projector_app/trusted_projector_annotator_ui.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/webui/projector_app/trusted_projector_annotator_ui.h"
-
-#include "ash/public/cpp/projector/projector_annotator_controller.h"
-#include "ash/webui/grit/ash_projector_annotator_trusted_resources.h"
-#include "ash/webui/grit/ash_projector_annotator_trusted_resources_map.h"
-#include "ash/webui/grit/ash_projector_common_resources.h"
-#include "ash/webui/grit/ash_projector_common_resources_map.h"
-#include "ash/webui/projector_app/annotator_page_handler_impl.h"
-#include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "content/public/common/url_constants.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "services/network/public/mojom/content_security_policy.mojom.h"
-#include "url/gurl.h"
-
-namespace ash {
-
-namespace {
-
-void CreateAndAddProjectorAnnotatorHTMLSource(content::WebUI* web_ui) {
-  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
-      web_ui->GetWebContents()->GetBrowserContext(),
-      kChromeUIProjectorAnnotatorHost);
-
-  // TODO(b/216523790): Split trusted annotator resources into a separate
-  // bundle.
-  source->AddResourcePaths(
-      base::make_span(kAshProjectorAnnotatorTrustedResources,
-                      kAshProjectorAnnotatorTrustedResourcesSize));
-  source->AddResourcePaths(base::make_span(kAshProjectorCommonResources,
-                                           kAshProjectorCommonResourcesSize));
-  source->AddResourcePath(
-      "", IDR_ASH_PROJECTOR_ANNOTATOR_TRUSTED_ANNOTATOR_EMBEDDER_HTML);
-
-  std::string csp =
-      std::string("frame-src ") + kChromeUIUntrustedAnnotatorUrl + ";";
-  // Allow use of SharedArrayBuffer (required by wasm code in the iframe guest).
-  source->OverrideCrossOriginOpenerPolicy("same-origin");
-  source->OverrideCrossOriginEmbedderPolicy("require-corp");
-
-  source->OverrideContentSecurityPolicy(
-      network::mojom::CSPDirectiveName::FrameSrc, csp);
-
-  source->OverrideContentSecurityPolicy(
-      network::mojom::CSPDirectiveName::TrustedTypes,
-      "trusted-types polymer-html-literal "
-      "polymer-template-event-attribute-policy;");
-}
-
-}  // namespace
-
-TrustedProjectorAnnotatorUI::TrustedProjectorAnnotatorUI(
-    content::WebUI* web_ui,
-    const GURL& url,
-    PrefService* pref_service)
-    : MojoBubbleWebUIController(web_ui, /*enable_chrome_send=*/false) {
-  CreateAndAddProjectorAnnotatorHTMLSource(web_ui);
-
-  // The Annotator and Projector SWA embed contents in a sandboxed
-  // chrome-untrusted:// iframe.
-  web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme);
-}
-
-TrustedProjectorAnnotatorUI::~TrustedProjectorAnnotatorUI() = default;
-
-void TrustedProjectorAnnotatorUI::BindInterface(
-    mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandlerFactory>
-        receiver) {
-  if (receiver_.is_bound()) {
-    receiver_.reset();
-  }
-  receiver_.Bind(std::move(receiver));
-}
-
-void TrustedProjectorAnnotatorUI::Create(
-    mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandler>
-        annotator_handler,
-    mojo::PendingRemote<annotator::mojom::AnnotatorPage> annotator) {
-  // Multiple WebUIs (and therefore TrustedProjectorAnnotatorUIs) are created
-  // for a single Projector recording session, so a new AnnotatorMessageHandler
-  // needs to be created each time and attached to the new WebUI. The new
-  // handler is then referenced in ProjectorClientImpl.
-  handler_ = std::make_unique<AnnotatorPageHandlerImpl>(
-      std::move(annotator_handler), std::move(annotator), web_ui());
-}
-
-WEB_UI_CONTROLLER_TYPE_IMPL(TrustedProjectorAnnotatorUI)
-
-}  // namespace ash
diff --git a/ash/webui/projector_app/trusted_projector_annotator_ui.h b/ash/webui/projector_app/trusted_projector_annotator_ui.h
deleted file mode 100644
index ea79b58..0000000
--- a/ash/webui/projector_app/trusted_projector_annotator_ui.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WEBUI_PROJECTOR_APP_TRUSTED_PROJECTOR_ANNOTATOR_UI_H_
-#define ASH_WEBUI_PROJECTOR_APP_TRUSTED_PROJECTOR_ANNOTATOR_UI_H_
-
-#include "ash/webui/projector_app/mojom/annotator.mojom.h"
-#include "content/public/browser/web_ui_controller.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "ui/webui/mojo_bubble_web_ui_controller.h"
-
-class GURL;
-class PrefService;
-
-namespace ash {
-
-class AnnotatorPageHandlerImpl;
-
-// The implementation for the Projector annotator for screen recording
-// annotations.
-class TrustedProjectorAnnotatorUI
-    : public ui::MojoBubbleWebUIController,
-      annotator::mojom::AnnotatorPageHandlerFactory {
- public:
-  TrustedProjectorAnnotatorUI(content::WebUI* web_ui,
-                              const GURL& url,
-                              PrefService* pref_service);
-  ~TrustedProjectorAnnotatorUI() override;
-  TrustedProjectorAnnotatorUI(const TrustedProjectorAnnotatorUI&) = delete;
-  TrustedProjectorAnnotatorUI& operator=(const TrustedProjectorAnnotatorUI&) =
-      delete;
-
-  void BindInterface(
-      mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandlerFactory>
-          factory);
-
- private:
-  WEB_UI_CONTROLLER_TYPE_DECL();
-
-  // annotator::mojom::AnnotatorPageHandlerFactory:
-  void Create(
-      mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandler>
-          annotator_handler,
-      mojo::PendingRemote<annotator::mojom::AnnotatorPage> annotator) override;
-
-  mojo::Receiver<annotator::mojom::AnnotatorPageHandlerFactory> receiver_{this};
-
-  // Handler for requests coming from the web_ui.
-  std::unique_ptr<AnnotatorPageHandlerImpl> handler_;
-};
-
-}  // namespace ash
-
-#endif  // ASH_WEBUI_PROJECTOR_APP_TRUSTED_PROJECTOR_ANNOTATOR_UI_H_
diff --git a/ash/webui/projector_app/annotator_page_handler_impl.cc b/ash/webui/projector_app/untrusted_annotator_page_handler_impl.cc
similarity index 67%
rename from ash/webui/projector_app/annotator_page_handler_impl.cc
rename to ash/webui/projector_app/untrusted_annotator_page_handler_impl.cc
index 4beb9ec3..3328a30b1 100644
--- a/ash/webui/projector_app/annotator_page_handler_impl.cc
+++ b/ash/webui/projector_app/untrusted_annotator_page_handler_impl.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/webui/projector_app/annotator_page_handler_impl.h"
+#include "ash/webui/projector_app/untrusted_annotator_page_handler_impl.h"
 
 #include <memory>
 
 #include "ash/public/cpp/projector/annotator_tool.h"
 #include "ash/public/cpp/projector/projector_controller.h"
-#include "ash/webui/projector_app/mojom/annotator.mojom.h"
+#include "ash/webui/projector_app/mojom/untrusted_annotator.mojom.h"
 #include "ash/webui/projector_app/projector_app_client.h"
 #include "ash/webui/projector_app/public/mojom/annotator_structs.mojom.h"
 #include "base/check.h"
@@ -18,10 +18,10 @@
 
 namespace ash {
 
-AnnotatorPageHandlerImpl::AnnotatorPageHandlerImpl(
-    mojo::PendingReceiver<annotator::mojom::AnnotatorPageHandler>
+UntrustedAnnotatorPageHandlerImpl::UntrustedAnnotatorPageHandlerImpl(
+    mojo::PendingReceiver<annotator::mojom::UntrustedAnnotatorPageHandler>
         annotator_handler,
-    mojo::PendingRemote<annotator::mojom::AnnotatorPage> annotator,
+    mojo::PendingRemote<annotator::mojom::UntrustedAnnotatorPage> annotator,
     content::WebUI* web_ui)
     : annotator_remote_(std::move(annotator)),
       annotator_handler_receiver_(this, std::move(annotator_handler)),
@@ -29,11 +29,11 @@
   ProjectorAppClient::Get()->SetAnnotatorPageHandler(this);
 }
 
-AnnotatorPageHandlerImpl::~AnnotatorPageHandlerImpl() {
+UntrustedAnnotatorPageHandlerImpl::~UntrustedAnnotatorPageHandlerImpl() {
   ProjectorAppClient::Get()->ResetAnnotatorPageHandler(this);
 }
 
-void AnnotatorPageHandlerImpl::SetTool(const AnnotatorTool& tool) {
+void UntrustedAnnotatorPageHandlerImpl::SetTool(const AnnotatorTool& tool) {
   auto mojo_tool = annotator::mojom::AnnotatorTool::New();
   mojo_tool->color = tool.GetColorHexString();
   mojo_tool->tool = tool.GetToolString();
@@ -41,19 +41,19 @@
   annotator_remote_->SetTool(std::move(mojo_tool));
 }
 
-void AnnotatorPageHandlerImpl::Undo() {
+void UntrustedAnnotatorPageHandlerImpl::Undo() {
   annotator_remote_->Undo();
 }
 
-void AnnotatorPageHandlerImpl::Redo() {
+void UntrustedAnnotatorPageHandlerImpl::Redo() {
   annotator_remote_->Redo();
 }
 
-void AnnotatorPageHandlerImpl::Clear() {
+void UntrustedAnnotatorPageHandlerImpl::Clear() {
   annotator_remote_->Clear();
 }
 
-void AnnotatorPageHandlerImpl::OnUndoRedoAvailabilityChanged(
+void UntrustedAnnotatorPageHandlerImpl::OnUndoRedoAvailabilityChanged(
     bool undo_available,
     bool redo_available) {
   // ProjectorController is created when ash::Shell::Init is called and is
@@ -63,20 +63,11 @@
                                                             redo_available);
 }
 
-void AnnotatorPageHandlerImpl::OnCanvasInitialized(bool success) {
+void UntrustedAnnotatorPageHandlerImpl::OnCanvasInitialized(bool success) {
   // ProjectorController is created when ash::Shell::Init is called and is
   // destroyed when ash::Shell is destroyed. Therefore, ProjectorController
   // is available when this WebUI is showing.
   ProjectorController::Get()->OnCanvasInitialized(success);
 }
 
-void AnnotatorPageHandlerImpl::OnError(
-    const std::vector<std::string>& messages) {
-  for (const auto& message : messages) {
-    LOG(ERROR) << message;
-  }
-
-  // TODO(b/239979179): Consider reloading the webcontent.
-}
-
 }  // namespace ash
diff --git a/ash/webui/projector_app/untrusted_annotator_page_handler_impl.h b/ash/webui/projector_app/untrusted_annotator_page_handler_impl.h
new file mode 100644
index 0000000..9f194f0
--- /dev/null
+++ b/ash/webui/projector_app/untrusted_annotator_page_handler_impl.h
@@ -0,0 +1,66 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WEBUI_PROJECTOR_APP_UNTRUSTED_ANNOTATOR_PAGE_HANDLER_IMPL_H_
+#define ASH_WEBUI_PROJECTOR_APP_UNTRUSTED_ANNOTATOR_PAGE_HANDLER_IMPL_H_
+
+#include "ash/public/cpp/projector/projector_annotator_controller.h"
+#include "ash/webui/projector_app/mojom/untrusted_annotator.mojom.h"
+#include "base/functional/callback.h"
+#include "base/values.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace content {
+class WebUI;
+}  // namespace content
+
+namespace ash {
+
+struct AnnotatorTool;
+
+// Handles communication with the Annotator WebUI (i.e.
+// chrome-untrusted://projector/annotator/annotator_embedder.html)
+class UntrustedAnnotatorPageHandlerImpl
+    : public annotator::mojom::UntrustedAnnotatorPageHandler {
+ public:
+  UntrustedAnnotatorPageHandlerImpl(
+      mojo::PendingReceiver<annotator::mojom::UntrustedAnnotatorPageHandler>
+          annotator_handler,
+      mojo::PendingRemote<annotator::mojom::UntrustedAnnotatorPage> annotator,
+      content::WebUI* web_ui);
+  UntrustedAnnotatorPageHandlerImpl(const UntrustedAnnotatorPageHandlerImpl&) =
+      delete;
+  UntrustedAnnotatorPageHandlerImpl& operator=(
+      const UntrustedAnnotatorPageHandlerImpl&) = delete;
+  ~UntrustedAnnotatorPageHandlerImpl() override;
+
+  // Called by ProjectorAppClient.
+  void SetTool(const AnnotatorTool& tool);
+  void Undo();
+  void Redo();
+  void Clear();
+
+  // annotator::mojom::AnnotatorHandler:
+  void OnUndoRedoAvailabilityChanged(bool undo_available,
+                                     bool redo_available) override;
+  void OnCanvasInitialized(bool success) override;
+
+  content::WebUI* get_web_ui_for_test() { return web_ui_; }
+
+ private:
+  mojo::Remote<annotator::mojom::UntrustedAnnotatorPage> annotator_remote_;
+  mojo::Receiver<annotator::mojom::UntrustedAnnotatorPageHandler>
+      annotator_handler_receiver_;
+
+  // The WebUI that owns the TrustedProjectorAnnotatorUI that owns this
+  // instance.
+  content::WebUI* const web_ui_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_WEBUI_PROJECTOR_APP_UNTRUSTED_ANNOTATOR_PAGE_HANDLER_IMPL_H_
diff --git a/ash/webui/projector_app/untrusted_projector_annotator_ui.cc b/ash/webui/projector_app/untrusted_projector_annotator_ui.cc
index 10bab51d..3cb065d 100644
--- a/ash/webui/projector_app/untrusted_projector_annotator_ui.cc
+++ b/ash/webui/projector_app/untrusted_projector_annotator_ui.cc
@@ -10,6 +10,7 @@
 #include "ash/webui/grit/ash_projector_common_resources_map.h"
 #include "ash/webui/media_app_ui/buildflags.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
+#include "ash/webui/projector_app/untrusted_annotator_page_handler_impl.h"
 #include "chromeos/grit/chromeos_projector_app_bundle_resources.h"
 #include "chromeos/grit/chromeos_projector_app_bundle_resources_map.h"
 #include "content/public/browser/web_contents.h"
@@ -87,9 +88,6 @@
   // Loading WASM in chrome-untrusted://projector-annotator/annotator/ink.js is
   // not compatible with trusted types.
   source->DisableTrustedTypesCSP();
-
-  source->AddFrameAncestor(GURL(kChromeUITrustedAnnotatorUrl));
-
   delegate->PopulateLoadTimeData(source);
   source->UseStringsJs();
 }
@@ -105,4 +103,23 @@
 
 UntrustedProjectorAnnotatorUI::~UntrustedProjectorAnnotatorUI() = default;
 
+void UntrustedProjectorAnnotatorUI::BindInterface(
+    mojo::PendingReceiver<
+        annotator::mojom::UntrustedAnnotatorPageHandlerFactory> factory) {
+  if (receiver_.is_bound()) {
+    receiver_.reset();
+  }
+  receiver_.Bind(std::move(factory));
+}
+
+void UntrustedProjectorAnnotatorUI::Create(
+    mojo::PendingReceiver<annotator::mojom::UntrustedAnnotatorPageHandler>
+        annotator_handler,
+    mojo::PendingRemote<annotator::mojom::UntrustedAnnotatorPage> annotator) {
+  handler_ = std::make_unique<UntrustedAnnotatorPageHandlerImpl>(
+      std::move(annotator_handler), std::move(annotator), web_ui());
+}
+
+WEB_UI_CONTROLLER_TYPE_IMPL(UntrustedProjectorAnnotatorUI)
+
 }  // namespace ash
diff --git a/ash/webui/projector_app/untrusted_projector_annotator_ui.h b/ash/webui/projector_app/untrusted_projector_annotator_ui.h
index ead7586e..3616ac5 100644
--- a/ash/webui/projector_app/untrusted_projector_annotator_ui.h
+++ b/ash/webui/projector_app/untrusted_projector_annotator_ui.h
@@ -5,6 +5,10 @@
 #ifndef ASH_WEBUI_PROJECTOR_APP_UNTRUSTED_PROJECTOR_ANNOTATOR_UI_H_
 #define ASH_WEBUI_PROJECTOR_APP_UNTRUSTED_PROJECTOR_ANNOTATOR_UI_H_
 
+#include "ash/webui/projector_app/mojom/untrusted_annotator.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "ui/webui/untrusted_web_ui_controller.h"
 
 namespace content {
@@ -13,6 +17,8 @@
 
 namespace ash {
 
+class UntrustedAnnotatorPageHandlerImpl;
+
 // A delegate used during data source creation to expose some //chrome
 // functionality to the data source
 class UntrustedProjectorAnnotatorUIDelegate {
@@ -23,7 +29,9 @@
 };
 
 // The webui for chrome-untrusted://projector-annotator.
-class UntrustedProjectorAnnotatorUI : public ui::UntrustedWebUIController {
+class UntrustedProjectorAnnotatorUI
+    : public ui::UntrustedWebUIController,
+      public annotator::mojom::UntrustedAnnotatorPageHandlerFactory {
  public:
   // UntrustedProjectorAnnotatorUI does not store the passed in
   // `UntrustedProjectorAnnotatorUIDelegate`.
@@ -34,6 +42,26 @@
   UntrustedProjectorAnnotatorUI& operator=(
       const UntrustedProjectorAnnotatorUI&) = delete;
   ~UntrustedProjectorAnnotatorUI() override;
+
+  void BindInterface(
+      mojo::PendingReceiver<
+          annotator::mojom::UntrustedAnnotatorPageHandlerFactory> factory);
+
+ private:
+  // annotator::mojom::UntrustedAnnotatorPageHandlerFactory:
+  void Create(
+      mojo::PendingReceiver<annotator::mojom::UntrustedAnnotatorPageHandler>
+          annotator_handler,
+      mojo::PendingRemote<annotator::mojom::UntrustedAnnotatorPage> annotator)
+      override;
+
+  mojo::Receiver<annotator::mojom::UntrustedAnnotatorPageHandlerFactory>
+      receiver_{this};
+
+  // Handler for requests coming from the web_ui.
+  std::unique_ptr<UntrustedAnnotatorPageHandlerImpl> handler_;
+
+  WEB_UI_CONTROLLER_TYPE_DECL();
 };
 
 }  // namespace ash
diff --git a/ash/webui/resources/BUILD.gn b/ash/webui/resources/BUILD.gn
index 04dd34df..f162d1f 100644
--- a/ash/webui/resources/BUILD.gn
+++ b/ash/webui/resources/BUILD.gn
@@ -330,14 +330,6 @@
   deps = [ "//ash/webui/projector_app/resources/annotator/untrusted:build_untrusted_grd" ]
 }
 
-# Resources used by chrome://projector-annotator SWA.
-ash_generated_grit("projector_annotator_trusted_resources") {
-  source = "$root_gen_dir/ash/webui/projector_app/resources/annotator/trusted/ash_projector_annotator_trusted_resources.grd"
-  deps = [
-    "//ash/webui/projector_app/resources/annotator/trusted:build_trusted_grd",
-  ]
-}
-
 # Resources used by chrome://projector and chrome://projector-annotator SWA.
 ash_generated_grit("projector_common_resources") {
   source = "$root_gen_dir/ash/webui/projector_app/resources/common/ash_projector_common_resources.grd"
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc
index b73522c..4e80071 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc
@@ -42,6 +42,25 @@
 using ::ash::shortcut_customization::mojom::SimpleAccelerator;
 using ::ash::shortcut_customization::mojom::SimpleAcceleratorPtr;
 using mojom::AcceleratorConfigResult;
+using HiddenAcceleratorMap =
+    std::map<AcceleratorActionId, std::vector<ui::Accelerator>>;
+
+// Raw accelerator data may result in the same shortcut being displayed multiple
+// times in the frontend. GetHiddenAcceleratorMap() is used to collect such
+// accelerators and hide them from display.
+const HiddenAcceleratorMap& GetHiddenAcceleratorMap() {
+  static auto hiddenAcceleratorMap = base::NoDestructor<HiddenAcceleratorMap>(
+      {{TOGGLE_APP_LIST,
+        {ui::Accelerator(ui::VKEY_BROWSER_SEARCH, ui::EF_NONE,
+                         ui::Accelerator::KeyState::PRESSED),
+         ui::Accelerator(ui::VKEY_BROWSER_SEARCH, ui::EF_SHIFT_DOWN,
+                         ui::Accelerator::KeyState::PRESSED),
+         ui::Accelerator(ui::VKEY_LWIN, ui::EF_NONE,
+                         ui::Accelerator::KeyState::RELEASED),
+         ui::Accelerator(ui::VKEY_LWIN, ui::EF_SHIFT_DOWN,
+                         ui::Accelerator::KeyState::RELEASED)}}});
+  return *hiddenAcceleratorMap;
+}
 
 // Gets the parts of the string that don't contain replacements.
 // Ex: "Press and " -> ["Press ", " and "]
@@ -126,6 +145,17 @@
   return result;
 }
 
+bool IsAcceleratorHidden(AcceleratorActionId action_id,
+                         const ui::Accelerator& accelerator) {
+  const auto& iter = GetHiddenAcceleratorMap().find(action_id);
+  if (iter == GetHiddenAcceleratorMap().end()) {
+    return false;
+  }
+  const std::vector<ui::Accelerator>& hidden_accelerators = iter->second;
+  return std::find(hidden_accelerators.begin(), hidden_accelerators.end(),
+                   accelerator) != hidden_accelerators.end();
+}
+
 mojom::StandardAcceleratorPropertiesPtr CreateStandardAcceleratorProps(
     const ui::Accelerator& accelerator) {
   return mojom::StandardAcceleratorProperties::New(
@@ -535,6 +565,9 @@
     }
 
     for (const auto& accelerator : accelerators) {
+      if (IsAcceleratorHidden(layout_info.action_id, accelerator)) {
+        continue;
+      }
       // TODO(jimmyxgong): Check pref storage to determine whether the
       // AcceleratorType was user-added or default.
       CreateAndAppendAliasedAccelerators(
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
index 35134b1..28cdebf 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
@@ -533,6 +533,44 @@
   ValidateAcceleratorLayouts(provider_->GetAcceleratorLayoutInfos());
 }
 
+TEST_F(AcceleratorConfigurationProviderTest, FilterOutHiddenAccelerators) {
+  FakeAcceleratorsUpdatedMojoObserver mojo_observer;
+  SetUpObserver(&mojo_observer);
+  EXPECT_EQ(0, mojo_observer.num_times_notified());
+  EXPECT_EQ(0, observer_.num_times_notified());
+
+  const AcceleratorData test_data[] = {
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN,
+       SHOW_TASK_MANAGER},
+      // Accelerators that should be hidden from display.
+      {/*trigger_on_press=*/true, ui::VKEY_BROWSER_SEARCH, ui::EF_NONE,
+       TOGGLE_APP_LIST},
+      {/*trigger_on_press=*/true, ui::VKEY_BROWSER_SEARCH, ui::EF_SHIFT_DOWN,
+       TOGGLE_APP_LIST},
+      {/*trigger_on_press=*/false, ui::VKEY_LWIN, ui::EF_NONE, TOGGLE_APP_LIST},
+      {/*trigger_on_press=*/false, ui::VKEY_LWIN, ui::EF_SHIFT_DOWN,
+       TOGGLE_APP_LIST},
+  };
+
+  // Initialize with a set of accelerators that include hidden accelerators.
+  Shell::Get()->ash_accelerator_configuration()->Initialize(test_data);
+  base::RunLoop().RunUntilIdle();
+
+  const AcceleratorData expected_test_data[]{
+      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
+       CYCLE_FORWARD_MRU},
+      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN,
+       SHOW_TASK_MANAGER},
+  };
+  EXPECT_EQ(1, observer_.num_times_notified());
+
+  // Verify observer won't receive hidden accelerators.
+  ExpectMojomAcceleratorsEqual(mojom::AcceleratorSource::kAsh,
+                               expected_test_data, mojo_observer.config());
+}
+
 TEST_F(AcceleratorConfigurationProviderTest, TopRowKeyAcceleratorRemapped) {
   // Add a fake layout2 keyboard.
   ui::InputDevice fake_keyboard(
diff --git a/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.ts b/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.ts
index ada8008..08076fa 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/accelerator_row.ts
@@ -9,14 +9,14 @@
 import 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 
-import {assert} from 'chrome://resources/js/assert_ts.js';
 import {PolymerElementProperties} from 'chrome://resources/polymer/v3_0/polymer/interfaces.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './accelerator_row.html.js';
 import {getShortcutProvider} from './mojo_interface_provider.js';
-import {AcceleratorInfo, AcceleratorSource, LayoutStyle, ShortcutProviderInterface, TextAcceleratorPart} from './shortcut_types.js';
-import {isCustomizationDisabled, isTextAcceleratorInfo} from './shortcut_utils.js';
+import {AcceleratorInfo, AcceleratorSource, LayoutStyle, ShortcutProviderInterface, TextAcceleratorInfo, TextAcceleratorPart} from './shortcut_types.js';
+import {isCustomizationDisabled} from './shortcut_utils.js';
+import {TextAcceleratorElement} from './text_accelerator.js';
 
 export type ShowEditDialogEvent = CustomEvent<{
   description: string,
@@ -138,14 +138,9 @@
         ));
   }
 
-  protected getTextAcceleratorParts(info: AcceleratorInfo[]):
+  protected getTextAcceleratorParts(infos: TextAcceleratorInfo[]):
       TextAcceleratorPart[] {
-    // For text based layout accelerators, we always expect this to be an array
-    // with a single element.
-    assert(info.length === 1);
-    const textAcceleratorInfo = info[0];
-    assert(isTextAcceleratorInfo(textAcceleratorInfo));
-    return textAcceleratorInfo.layoutProperties.textAccelerator.parts;
+    return TextAcceleratorElement.getTextAcceleratorParts(infos);
   }
 
   static get template(): HTMLTemplateElement {
diff --git a/ash/webui/shortcut_customization_ui/resources/js/fake_data.ts b/ash/webui/shortcut_customization_ui/resources/js/fake_data.ts
index 72e1ea2..a442b6ce 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/fake_data.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/fake_data.ts
@@ -12,7 +12,7 @@
   internalValue: BigInt(0),
 };
 
-const newTabAccelerator: MojoAcceleratorInfo = {
+const newTabAcceleratorInfo: MojoAcceleratorInfo = {
   type: AcceleratorType.kDefault,
   state: AcceleratorState.kEnabled,
   locked: true,
@@ -31,7 +31,7 @@
   },
 };
 
-const cycleTabsAccelerator: MojoAcceleratorInfo = {
+const cycleTabsAcceleratorInfo: MojoAcceleratorInfo = {
   type: AcceleratorType.kDefault,
   state: AcceleratorState.kEnabled,
   locked: true,
@@ -145,15 +145,15 @@
   // TODO(michaelcheco): Separate Browser and Ambient accelerators.
   [AcceleratorSource.kAmbient]: {
     // New Tab
-    [0]: [newTabAccelerator],
-    [1]: [cycleTabsAccelerator],
+    [0]: [newTabAcceleratorInfo],
+    [1]: [cycleTabsAcceleratorInfo],
   },
 };
 
 export const fakeAmbientConfig: MojoAcceleratorConfig = {
   [AcceleratorSource.kAmbient]: {
-    [0]: [newTabAccelerator],
-    [1]: [cycleTabsAccelerator],
+    [0]: [newTabAcceleratorInfo],
+    [1]: [cycleTabsAcceleratorInfo],
   },
 };
 
@@ -298,6 +298,19 @@
 export const SnapWindowLeftSearchResult: MojoSearchResult =
     fakeSearchResults[0];
 
+export const CycleTabsTextSearchResult: MojoSearchResult = {
+  acceleratorLayoutInfo: {
+    category: AcceleratorCategory.kGeneral,
+    subCategory: AcceleratorSubcategory.kApps,
+    description: stringToMojoString16('Click or tap shelf icons 1-8'),
+    style: LayoutStyle.kText,
+    source: AcceleratorSource.kAsh,
+    action: 1,
+  },
+  acceleratorInfos: [cycleTabsAcceleratorInfo],
+  relevanceScore: 0.95,
+};
+
 // The following code is used to add fake accelerator entries for each icon.
 // When useFakeProvider is true, this will display all available icons for
 // the purposes of debugging.
diff --git a/ash/webui/shortcut_customization_ui/resources/js/input_key.html b/ash/webui/shortcut_customization_ui/resources/js/input_key.html
index 1b1b460..d3fcd20 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/input_key.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/input_key.html
@@ -12,6 +12,11 @@
     padding-inline: 8px;
   }
 
+  :host([narrow]) .key-container {
+    margin-inline-end: 4px;
+    padding-inline: 4px;
+  }
+
   #key-text {
     color: var(--cros-text-color-secondary);
   }
@@ -35,6 +40,18 @@
     box-shadow: 0 1px 1px var(--cros-bg-color-dropped-elevation-1);
   }
 
+  :host([highlighted][key-state='modifier-selected']) div.key-container {
+    background-color: var(--cros-color-prominent);
+  }
+
+  :host([highlighted][key-state='modifier-selected']) #key-text {
+    color: var(--cros-button-label-color-primary);
+  }
+
+  :host([highlighted][key-state='alpha-numeric-selected']) div.key-container {
+    background-color: var(--cros-bg-color);
+  }
+
   :host(#ctrlKey) .key-container,
   :host(#altKey) .key-container,
   :host(#shiftKey) .key-container,
diff --git a/ash/webui/shortcut_customization_ui/resources/js/input_key.ts b/ash/webui/shortcut_customization_ui/resources/js/input_key.ts
index 6f0c7c9..79c9ccc 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/input_key.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/input_key.ts
@@ -94,11 +94,29 @@
         value: KeyInputState.NOT_SELECTED,
         reflectToAttribute: true,
       },
+
+      // If this property is true, the spacing between keys will be narrower
+      // than usual.
+      narrow: {
+        type: Boolean,
+        value: false,
+        reflectToAttribute: true,
+      },
+
+      // If this property is true, keys will be styled with the bolder highlight
+      // background.
+      highlighted: {
+        type: Boolean,
+        value: false,
+        reflectToAttribute: true,
+      },
     };
   }
 
   key: string;
   keyState: KeyInputState;
+  narrow: boolean;
+  highlighted: boolean;
   private lookupManager: AcceleratorLookupManager =
       AcceleratorLookupManager.getInstance();
 
diff --git a/ash/webui/shortcut_customization_ui/resources/js/search/search_box.html b/ash/webui/shortcut_customization_ui/resources/js/search/search_box.html
index 5b491db..fc5f529 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/search/search_box.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/search/search_box.html
@@ -2,6 +2,8 @@
   :host {
     --cr-toolbar-search-field-background:
         var(--cros-toolbar-search-bg-color);
+    --cr-toolbar-icon-container-size: 32px;
+    --cr-toolbar-icon-margin: 8px 16px;
 
     --separator-height: 8px;
     -webkit-tap-highlight-color: transparent;
@@ -56,7 +58,7 @@
     border-radius: 0 0 20px 20px;
     box-shadow: var(--cr-elevation-3);
     display: table;
-    padding-bottom: 8px;
+    padding-bottom: 16px;
     width: var(--cr-toolbar-field-width);
   }
 
diff --git a/ash/webui/shortcut_customization_ui/resources/js/search/search_result_row.html b/ash/webui/shortcut_customization_ui/resources/js/search/search_result_row.html
index 0e94abd..611fa9cf 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/search/search_result_row.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/search/search_result_row.html
@@ -13,23 +13,65 @@
 
   #searchResultRow {
     align-items: center;
+    cursor: pointer;
     display: flex;
     height: 48px;
     justify-content: center;
   }
 
-  #searchResultText {
+  #searchResultRowInner {
     align-items: center;
     display: flex;
-    flex-grow: 1;
+    flex-direction: row;
     height: 100%;
-    padding-inline-start: 24px;
+    padding-inline-start: 16px;
+    width: 100%;
+  }
+
+  #description {
+    /* Fade out long descriptions. */
+    -webkit-mask-image: linear-gradient(to left, transparent, white 32px);
+    flex: 1;
+    margin-inline-end: 16px;
+    overflow: hidden;
+    white-space: nowrap;
+  }
+
+  #accelerators {
+    /* Fade out long accelerators. */
+    -webkit-mask-image: linear-gradient(to left, transparent, white 32px);
+    align-items: center;
+    display: flex;
+    flex: 1;
+    height: 100%;
+    overflow: hidden;
+  }
+
+  #actionIcon {
+    margin: var(--cr-toolbar-icon-margin);
+    width: var(--cr-toolbar-icon-container-size);
   }
 </style>
 
 <div id="searchResultRow" focus-row-container>
-  <div id="searchResultText" aria-disabled="true" selectable focus-row-control
-      focus-type="rowWrapper">
-        [[getSearchResultText(searchResult)]]
-  </div>
+  <div focus-row-control
+      focus-type="rowWrapper"
+      id="searchResultRowInner"
+      aria-disabled="true"
+      selectable>
+      <div id="description">[[getSearchResultDescription(searchResult)]]</div>
+      <div id="accelerators">
+        <template is="dom-if" if="[[isDefaultLayout(searchResult)]]">
+          <!-- TODO(cambickel) Add standard accelerator keys here. -->
+          <div></div>
+        </template>
+        <template is="dom-if" if="[[isTextLayout(searchResult)]]">
+          <text-accelerator parts="[[getTextAcceleratorParts(searchResult)]]"
+              highlighted="[[selected]]" narrow>
+          </text-accelerator>
+        </template>
+        <div id="gradient"></div>
+      </div>
+      <iron-icon id="actionIcon" icon="cr:arrow-forward"></iron-icon>
+    </div>
 </div>
diff --git a/ash/webui/shortcut_customization_ui/resources/js/search/search_result_row.ts b/ash/webui/shortcut_customization_ui/resources/js/search/search_result_row.ts
index 878c43e..387306d 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/search/search_result_row.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/search/search_result_row.ts
@@ -2,13 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import '../text_accelerator.js';
 
 import {FocusRowMixin} from 'chrome://resources/cr_elements/focus_row_mixin.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {PolymerElementProperties} from 'chrome://resources/polymer/v3_0/polymer/interfaces.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {MojoSearchResult} from 'js/shortcut_types.js';
 
 import {mojoString16ToString} from '../mojo_utils.js';
+import {LayoutStyle, MojoSearchResult, TextAcceleratorInfo, TextAcceleratorPart} from '../shortcut_types.js';
+import {isTextAcceleratorInfo} from '../shortcut_utils.js';
+import {TextAcceleratorElement} from '../text_accelerator.js';
 
 import {getTemplate} from './search_result_row.html.js';
 
@@ -26,7 +30,6 @@
 
   static get properties(): PolymerElementProperties {
     return {
-      // TODO(longbowei): This is an incomplete type. Update it in the future.
       searchResult: {
         type: Object,
       },
@@ -42,13 +45,28 @@
   searchResult: MojoSearchResult;
   selected: boolean;
 
-  private getSearchResultText(): string {
+  static get template(): HTMLTemplateElement {
+    return getTemplate();
+  }
+
+  private getSearchResultDescription(): string {
     return mojoString16ToString(
         this.searchResult.acceleratorLayoutInfo.description);
   }
 
-  static get template(): HTMLTemplateElement {
-    return getTemplate();
+  private isDefaultLayout(): boolean {
+    return this.searchResult.acceleratorLayoutInfo.style ===
+        LayoutStyle.kDefault;
+  }
+
+  private isTextLayout(): boolean {
+    return !this.isDefaultLayout();
+  }
+
+  private getTextAcceleratorParts(): TextAcceleratorPart[] {
+    assert(isTextAcceleratorInfo(this.searchResult.acceleratorInfos[0]));
+    return TextAcceleratorElement.getTextAcceleratorParts(
+        this.searchResult.acceleratorInfos as TextAcceleratorInfo[]);
   }
 }
 
diff --git a/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.html b/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.html
index 32df5f5..a4d4ded7 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.html
+++ b/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.html
@@ -3,6 +3,10 @@
     margin-inline-end: 8px;
   }
 
+  :host([narrow]) .spacing {
+    margin-inline-end: 4px;
+  }
+
   #text-wrapper {
     align-items: center;
     display: flex;
@@ -15,6 +19,11 @@
     flex-wrap: wrap;
   }
 
+  :host([narrow]) .parts-container {
+    flex-wrap: nowrap;
+    white-space: nowrap;
+  }
+
   iron-icon {
     --iron-icon-width: 16px;
   }
diff --git a/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.ts b/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.ts
index ca0a969b..5ee831ab 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/text_accelerator.ts
@@ -5,14 +5,15 @@
 import './input_key.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {IronIconElement} from 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import {PolymerElementProperties} from 'chrome://resources/polymer/v3_0/polymer/interfaces.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {InputKeyElement, KeyInputState} from './input_key.js';
 import {mojoString16ToString} from './mojo_utils.js';
-import {TextAcceleratorPart, TextAcceleratorPartType} from './shortcut_types.js';
-import {isCustomizationDisabled} from './shortcut_utils.js';
+import {TextAcceleratorInfo, TextAcceleratorPart, TextAcceleratorPartType} from './shortcut_types.js';
+import {isCustomizationDisabled, isTextAcceleratorInfo} from './shortcut_utils.js';
 import {getTemplate} from './text_accelerator.html.js';
 
 /**
@@ -32,10 +33,40 @@
         type: Array,
         observer: TextAcceleratorElement.prototype.parseAndDisplayTextParts,
       },
+
+      // If this property is true, the spacing between keys will be narrower
+      // than usual.
+      narrow: {
+        type: Boolean,
+        value: false,
+        reflectToAttribute: true,
+      },
+
+      // If this property is true, keys will be styled with the bolder highlight
+      // background.
+      highlighted: {
+        type: Boolean,
+        value: false,
+        // Update the parts when the highlighted status changes so their style
+        // can be updated.
+        observer: TextAcceleratorElement.prototype.parseAndDisplayTextParts,
+      },
     };
   }
 
   parts: TextAcceleratorPart[];
+  narrow: boolean;
+  highlighted: boolean;
+
+  static getTextAcceleratorParts(info: TextAcceleratorInfo[]):
+      TextAcceleratorPart[] {
+    // For text based layout accelerators, we always expect this to be an array
+    // with a single element.
+    assert(info.length === 1);
+    const textAcceleratorInfo = info[0];
+    assert(isTextAcceleratorInfo(textAcceleratorInfo));
+    return textAcceleratorInfo.layoutProperties.textAccelerator.parts;
+  }
 
   private parseAndDisplayTextParts(): void {
     const container =
@@ -72,6 +103,8 @@
     const key = document.createElement('input-key');
     key.key = keyText;
     key.keyState = keyState;
+    key.narrow = this.narrow;
+    key.highlighted = this.highlighted;
     return key;
   }
 
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc
index 1f55151..ae3b32d 100644
--- a/ash/wm/desks/desk.cc
+++ b/ash/wm/desks/desk.cc
@@ -395,8 +395,21 @@
   auto* desks_controller = DesksController::Get();
   if (!is_desk_being_removed_ &&
       !desks_util::IsWindowVisibleOnAllWorkspaces(window)) {
-    window->SetProperty(aura::client::kWindowWorkspaceKey,
-                        desks_controller->GetDeskIndex(this));
+    // Setting the property for `kWindowWorkspaceKey` or
+    // `kDeskGuidKey` will trigger a save for the window state. To
+    // avoid doing this twice, we tell the window state to hold off on saving
+    // until we save the `kDeskGuidKey` value.
+    // TODO(b/265490703): We should eventually clean up this and
+    // `GetScopedIgnorePropertyChange` when unit tests no longer need this
+    // scoping to prevent double saves.
+    {
+      auto scoped_ignore_property_changes =
+          WindowState::Get(window)->GetScopedIgnorePropertyChange();
+      window->SetProperty(aura::client::kWindowWorkspaceKey,
+                          desks_controller->GetDeskIndex(this));
+    }
+
+    window->SetProperty(kDeskGuidKey, uuid_.AsLowercaseString());
   }
 
   MaybeIncrementWeeklyActiveDesks();
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 06b352f..00cfa1c 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -298,6 +298,7 @@
                                  .enabled()),
         source_(source),
         desk_close_type_(type) {
+    full_restore::SaveRemovingDeskGuid(desk_->uuid());
     desk_->set_is_desk_being_removed(true);
   }
 
@@ -312,6 +313,7 @@
       toast_manager->Cancel(toast_id_);
       DesksController::Get()->FinalizeDeskRemoval(this);
     }
+    full_restore::ResetRemovingDeskGuid();
   }
 
   const std::string& toast_id() const { return toast_id_; }
@@ -1698,6 +1700,10 @@
   }
 
   // Keep the removed desk's data alive until at least the end of this function.
+  // `MaybeCommitPendingDeskRemoval` at this point should have cleared
+  // `temporary_removed_desk_`. Otherwise, we may be resetting the wrong
+  // removing desk GUID in restore data.
+  CHECK(!temporary_removed_desk_);
   auto temporary_removed_desk = std::make_unique<RemovedDeskData>(
       std::move(*iter), removed_desk_index, source, close_type);
   auto* temporary_removed_desk_ptr = temporary_removed_desk.get();
diff --git a/ash/wm/window_restore/window_restore_util.cc b/ash/wm/window_restore/window_restore_util.cc
index 4f0f4324..028ab76 100644
--- a/ash/wm/window_restore/window_restore_util.cc
+++ b/ash/wm/window_restore/window_restore_util.cc
@@ -62,6 +62,12 @@
     window_info->activation_index = window_activation_index;
   window_info->window = window;
   window_info->desk_id = window->GetProperty(aura::client::kWindowWorkspaceKey);
+  const std::string* desk_guid = window->GetProperty(kDeskGuidKey);
+
+  // It's possible for the desk to no longer exist or not be found in the case of
+  // CloseAll.
+  window_info->desk_guid =
+      desk_guid ? base::GUID::ParseLowercase(*desk_guid) : base::GUID();
 
   // If override bounds and window state are available (in tablet mode), save
   // those bounds.
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 0b88722..34b3df19 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -853,6 +853,10 @@
       &WindowState::CheckAndRecordDragMaximizedBehavior);
 }
 
+base::AutoReset<bool> WindowState::GetScopedIgnorePropertyChange() {
+  return base::AutoReset<bool>(&ignore_property_change_, true);
+}
+
 void WindowState::CreateDragDetails(const gfx::PointF& point_in_parent,
                                     int window_component,
                                     ::wm::WindowMoveSource source) {
@@ -1362,7 +1366,7 @@
     }
     return;
   }
-  if (key == aura::client::kWindowWorkspaceKey) {
+  if (key == aura::client::kWindowWorkspaceKey || key == kDeskGuidKey) {
     // Save the window for window restore purposes unless
     // |ignore_property_change_| is true. Note that moving windows across
     // displays will also trigger a kWindowWorkspaceKey change, even if the
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index 56acc20..414d63b 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -14,6 +14,7 @@
 #include "ash/wm/drag_details.h"
 #include "ash/wm/multi_display/persistent_window_info.h"
 #include "ash/wm/wm_metrics.h"
+#include "base/auto_reset.h"
 #include "base/gtest_prod_util.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
@@ -441,6 +442,9 @@
   // mis-triggered drag to maximize behavior.
   void TrackDragToMaximizeBehavior();
 
+  // Allows for caller to prevent property changes within scope.
+  base::AutoReset<bool> GetScopedIgnorePropertyChange();
+
   // Returns a pointer to DragDetails during drag operations.
   const DragDetails* drag_details() const { return drag_details_.get(); }
   DragDetails* drag_details() { return drag_details_.get(); }
diff --git a/base/allocator/partition_allocator/page_allocator.h b/base/allocator/partition_allocator/page_allocator.h
index cdeebe8a..e134744f 100644
--- a/base/allocator/partition_allocator/page_allocator.h
+++ b/base/allocator/partition_allocator/page_allocator.h
@@ -66,6 +66,7 @@
 // these tags are used to name anonymous mappings.
 enum class PageTag {
   kFirst = 240,           // Minimum tag value.
+  kSimulation = 251,      // Memory simulator tool.
   kBlinkGC = 252,         // Blink GC pages.
   kPartitionAlloc = 253,  // PartitionAlloc, no matter the partition.
   kChromium = 254,        // Chromium page.
@@ -73,6 +74,12 @@
   kLast = kV8             // Maximum tag value.
 };
 
+// See
+// https://github.com/apple-oss-distributions/xnu/blob/5c2921b07a2480ab43ec66f5b9e41cb872bc554f/osfmk/mach/vm_statistics.h#L687
+static_assert(
+    static_cast<int>(PageTag::kLast) < 256,
+    "Tags are only 1 byte long on macOS, see vm_statistics.h in XNU.");
+
 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
 uintptr_t NextAlignedWithOffset(uintptr_t ptr,
                                 uintptr_t alignment,
diff --git a/base/fuchsia/fidl_event_handler_unittest.cc b/base/fuchsia/fidl_event_handler_unittest.cc
index 8f194da..3283c981 100644
--- a/base/fuchsia/fidl_event_handler_unittest.cc
+++ b/base/fuchsia/fidl_event_handler_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <fidl/base.testfidl/cpp/fidl.h>
 #include <fidl/fuchsia.logger/cpp/fidl.h>
-#include <lib/async/default.h>
 #include <lib/sys/cpp/component_context.h>
 
 #include "base/fuchsia/fuchsia_component_connect.h"
@@ -99,8 +98,7 @@
 
   {
     ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding(
-        ComponentContextForProcess()->outgoing().get(), &test_service_,
-        async_get_default_dispatcher());
+        ComponentContextForProcess()->outgoing().get(), &test_service_);
 
     ASSERT_EQ(ZX_OK, VerifyTestInterface(client));
   };
@@ -155,8 +153,7 @@
     {
       TestInterfaceNaturalImpl test_service;
       ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding(
-          ComponentContextForProcess()->outgoing().get(), &test_service,
-          async_get_default_dispatcher());
+          ComponentContextForProcess()->outgoing().get(), &test_service);
 
       ASSERT_EQ(ZX_OK, VerifyTestInterface(client));
     }
@@ -200,8 +197,7 @@
 
   {
     ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding(
-        ComponentContextForProcess()->outgoing().get(), &test_service_,
-        async_get_default_dispatcher());
+        ComponentContextForProcess()->outgoing().get(), &test_service_);
 
     ASSERT_EQ(ZX_OK, VerifyTestInterface(client));
   };
diff --git a/base/fuchsia/scoped_natural_service_binding_unittest.cc b/base/fuchsia/scoped_natural_service_binding_unittest.cc
index e5de263..a99a340 100644
--- a/base/fuchsia/scoped_natural_service_binding_unittest.cc
+++ b/base/fuchsia/scoped_natural_service_binding_unittest.cc
@@ -34,8 +34,7 @@
 // client.
 TEST_F(ScopedNaturalServiceBindingTest, ConnectTwice) {
   ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding(
-      ComponentContextForProcess()->outgoing().get(), &test_service_,
-      async_get_default_dispatcher());
+      ComponentContextForProcess()->outgoing().get(), &test_service_);
 
   auto stub =
       CreateTestInterfaceClient(test_context_.published_services_natural());
@@ -52,7 +51,7 @@
 
   ScopedNaturalServiceBinding<base_testfidl::TestInterface> new_service_binding(
       ComponentContextForProcess()->outgoing().get(), &test_service_,
-      async_get_default_dispatcher(), kInterfaceName);
+      kInterfaceName);
 
   auto stub = CreateTestInterfaceClient(
       test_context_.published_services_natural(), kInterfaceName);
@@ -69,8 +68,7 @@
 
   // Publish the test service to the "debug" directory.
   ScopedNaturalServiceBinding<base_testfidl::TestInterface>
-      debug_service_binding(debug_dir, &test_service_,
-                            async_get_default_dispatcher());
+      debug_service_binding(debug_dir, &test_service_);
 
   // Connect a `ClientEnd` to the "debug" subdirectory.
   auto debug_directory_endpoints =
@@ -97,8 +95,7 @@
 // clients reaches 0.
 TEST_F(ScopedNaturalServiceBindingTest, MultipleLastClientCallback) {
   ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding(
-      ComponentContextForProcess()->outgoing().get(), &test_service_,
-      async_get_default_dispatcher());
+      ComponentContextForProcess()->outgoing().get(), &test_service_);
   int disconnect_count = 0;
   binding.SetOnLastClientCallback(
       BindLambdaForTesting([&disconnect_count]() { ++disconnect_count; }));
@@ -132,8 +129,7 @@
 // clients reaches 0.
 TEST_F(ScopedNaturalServiceBindingTest, LastClientCallbackOnlyForLastClient) {
   ScopedNaturalServiceBinding<base_testfidl::TestInterface> binding(
-      ComponentContextForProcess()->outgoing().get(), &test_service_,
-      async_get_default_dispatcher());
+      ComponentContextForProcess()->outgoing().get(), &test_service_);
   int disconnect_count = 0;
   binding.SetOnLastClientCallback(
       BindLambdaForTesting([&disconnect_count]() { ++disconnect_count; }));
diff --git a/base/fuchsia/scoped_service_binding.h b/base/fuchsia/scoped_service_binding.h
index 540021b..3ddc6269 100644
--- a/base/fuchsia/scoped_service_binding.h
+++ b/base/fuchsia/scoped_service_binding.h
@@ -7,6 +7,9 @@
 
 #include <utility>
 
+// TODO(crbug.com/1427626): Remove this include once the explicit
+// async_get_default_dispatcher() is no longer needed.
+#include <lib/async/default.h>
 #include <lib/fidl/cpp/binding.h>
 #include <lib/fidl/cpp/binding_set.h>
 #include <lib/fidl/cpp/interface_request.h>
@@ -74,26 +77,32 @@
   ScopedNaturalServiceBinding(
       sys::OutgoingDirectory* outgoing_directory,
       fidl::Server<Protocol>* impl,
-      async_dispatcher_t* dispatcher,
       base::StringPiece name = fidl::DiscoverableProtocolName<Protocol>)
-      : publisher_(outgoing_directory,
-                   bindings_.CreateHandler(impl,
-                                           dispatcher,
-                                           [](fidl::UnbindInfo info) {}),
-                   name) {}
+      : publisher_(
+            outgoing_directory,
+            bindings_.CreateHandler(
+                impl,
+                // TODO(crbug.com/1427626): Remove this param once there's an
+                // overload of `CreateHandler` that doesn't require it.
+                async_get_default_dispatcher(),
+                [](fidl::UnbindInfo info) {}),
+            name) {}
 
   // Publishes a service in the specified |pseudo_dir|. |pseudo_dir| and |impl|
   // must outlive the binding. The service is unpublished on destruction.
   ScopedNaturalServiceBinding(
       vfs::PseudoDir* pseudo_dir,
       fidl::Server<Protocol>* impl,
-      async_dispatcher_t* dispatcher,
       base::StringPiece name = fidl::DiscoverableProtocolName<Protocol>)
-      : publisher_(pseudo_dir,
-                   bindings_.CreateHandler(impl,
-                                           dispatcher,
-                                           [](fidl::UnbindInfo info) {}),
-                   name) {}
+      : publisher_(
+            pseudo_dir,
+            bindings_.CreateHandler(
+                impl,
+                // TODO(crbug.com/1427626): Remove this param once there's an
+                // overload of `CreateHandler` that doesn't require it.
+                async_get_default_dispatcher(),
+                [](fidl::UnbindInfo info) {}),
+            name) {}
 
   ScopedNaturalServiceBinding(const ScopedNaturalServiceBinding&) = delete;
   ScopedNaturalServiceBinding& operator=(const ScopedNaturalServiceBinding&) =
diff --git a/base/fuchsia/system_product_info_unittest.cc b/base/fuchsia/system_product_info_unittest.cc
index 1c2cc87ac..518f2e3 100644
--- a/base/fuchsia/system_product_info_unittest.cc
+++ b/base/fuchsia/system_product_info_unittest.cc
@@ -38,7 +38,7 @@
                           sys::OutgoingDirectory* outgoing_services)
       : model_(model),
         manufacturer_(manufacturer),
-        binding_(outgoing_services, this, async_get_default_dispatcher()) {}
+        binding_(outgoing_services, this) {}
   FakeHardwareInfoProduct(const FakeHardwareInfoProduct&) = delete;
   FakeHardwareInfoProduct& operator=(const FakeHardwareInfoProduct&) = delete;
   ~FakeHardwareInfoProduct() override = default;
diff --git a/base/rand_util.h b/base/rand_util.h
index 0402453..38be943 100644
--- a/base/rand_util.h
+++ b/base/rand_util.h
@@ -20,6 +20,10 @@
 #include "third_party/boringssl/src/include/openssl/rand.h"
 #endif
 
+namespace memory_simulator {
+class MemoryHolder;
+}
+
 namespace base {
 
 namespace internal {
@@ -168,6 +172,10 @@
   // base::Rand*() is too high, using something more representative than a
   // microbenchmark.
 
+  // Uses the generator to fill memory pages with random content to make them
+  // hard to compress, in a simulation tool not bundled with Chrome. CPU
+  // overhead must be minimized to correctly measure memory effects.
+  friend class memory_simulator::MemoryHolder;
   // Uses the generator to sub-sample metrics.
   friend class MetricsSubSampler;
 
diff --git a/build/config/rust.gni b/build/config/rust.gni
index 2f4fe215..68949af 100644
--- a/build/config/rust.gni
+++ b/build/config/rust.gni
@@ -25,7 +25,7 @@
   # slowly and carefully and there may be no actual bugs there.
   enable_rust = is_linux && !is_official_build && !using_sanitizer &&
                 target_cpu != "x86" && !use_clang_coverage && is_clang &&
-                !is_castos && target_os == host_os
+                target_os == host_os
 
   # As we incrementally enable Rust on mainstream builders, we want to enable
   # the toolchain (by switching 'enable_rust' to true) while still disabling
@@ -47,13 +47,6 @@
   # on supported platforms and GN targets.
   enable_local_libstd = true
 
-  # When true, uses the locally-built std in all Rust targets.
-  #
-  # As an internal implementation detail this can be overridden on specific
-  # targets (e.g. to run build.rs scripts while building std), but this
-  # generally should not be done.
-  use_local_std_by_default = false
-
   # Chromium currently has a Rust toolchain for Android and Linux, but
   # if you wish to experiment on more platforms you can use this
   # argument to specify an alternative toolchain.
@@ -90,8 +83,17 @@
 }
 
 declare_args() {
-  # Individual Rust components. A separate declare_args so it can depend
-  # on the previous args.
+  # Use a separate declare_args so these variables' defaults can depend on the
+  # ones above.
+
+  # When true, uses the locally-built std in all Rust targets.
+  #
+  # As an internal implementation detail this can be overridden on specific
+  # targets (e.g. to run build.rs scripts while building std), but this
+  # generally should not be done.
+  use_local_std_by_default = enable_local_libstd && use_chromium_rust_toolchain
+
+  # Individual Rust components.
 
   # Conversions between Rust types and C++ types.
   enable_rust_base_conversions = enable_all_rust_features
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 788ae34..97488c08 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-12.20230327.1.1
+12.20230327.2.1
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index 5c7613d5..58ebf32 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 vars in //DEPS.
-  libcxx_revision = "79a510511b03dc9a9f378ebe07c18c622b5bf349"
+  libcxx_revision = "26ace673c4b9c7a352fa9db182256bb341b6c44f"
 }
diff --git a/cc/slim/layer.cc b/cc/slim/layer.cc
index ea96843..948c633 100644
--- a/cc/slim/layer.cc
+++ b/cc/slim/layer.cc
@@ -14,7 +14,6 @@
 #include "base/ranges/algorithm.h"
 #include "cc/layers/layer.h"
 #include "cc/paint/filter_operation.h"
-#include "cc/paint/filter_operations.h"
 #include "cc/slim/features.h"
 #include "cc/slim/layer_tree.h"
 #include "cc/slim/layer_tree_impl.h"
@@ -445,6 +444,10 @@
   return !filters_.empty();
 }
 
+cc::FilterOperations Layer::GetFilters() const {
+  return ToCcFilters(filters_);
+}
+
 int Layer::GetNumDrawingLayersInSubtree() const {
   return num_descendants_that_draw_content_ + (draws_content_ ? 1 : 0);
 }
@@ -463,8 +466,6 @@
     const gfx::Rect* clip_in_target,
     const gfx::Rect& visible_rect,
     float opacity) {
-  DCHECK(!HasFilters() || render_pass.filters.IsEmpty());
-  render_pass.filters = ToCcFilters(filters_);
   viz::SharedQuadState* quad_state =
       render_pass.CreateAndAppendSharedQuadState();
   const gfx::Rect layer_rect{bounds()};
diff --git a/cc/slim/layer.h b/cc/slim/layer.h
index 5fd3b501..8229599 100644
--- a/cc/slim/layer.h
+++ b/cc/slim/layer.h
@@ -11,6 +11,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
+#include "cc/paint/filter_operations.h"
 #include "cc/slim/filter.h"
 #include "cc/slim/frame_data.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -202,6 +203,7 @@
   gfx::Transform ComputeTransformToParent() const;
   absl::optional<gfx::Transform> ComputeTransformFromParent() const;
   bool HasFilters() const;
+  cc::FilterOperations GetFilters() const;
   // This method counts this layer, This is different from
   // `NumDescendantsThatDrawContent` which counts descendent layers only.
   int GetNumDrawingLayersInSubtree() const;
diff --git a/cc/slim/layer_tree_impl.cc b/cc/slim/layer_tree_impl.cc
index e700d775..1ca2a620 100644
--- a/cc/slim/layer_tree_impl.cc
+++ b/cc/slim/layer_tree_impl.cc
@@ -431,6 +431,7 @@
        /*parent_transform_to_target=*/gfx::Transform(),
        /*parent_clip_in_target=*/nullptr, gfx::RectF(device_viewport_rect_),
        /*opacity=*/1.0f);
+  render_pass->filters = root_->GetFilters();
 
   if (background_color_.fA &&
       !frame_data.occlusion_in_target.Contains(device_viewport_rect_)) {
@@ -560,7 +561,7 @@
   }
 
   std::unique_ptr<viz::CompositorRenderPass> new_pass;
-  gfx::Rect new_pass_content_bounds;
+  gfx::Rect new_pass_clip;
   // Scale can be applied when drawing layers into the new pass, or when
   // drawing the new pass into its target pass. Generally prefer the former to
   // avoid visual artifacts when scaling the output of the new pass. Therefore
@@ -596,23 +597,16 @@
         gfx::Transform::MakeScale(scale_to_new_pass.x(), scale_to_new_pass.y());
 
     // First clip in layer space, then transform to parent target space.
-    new_pass_content_bounds.set_size(layer.bounds());
-    new_pass_content_bounds.Intersect(gfx::ToEnclosedRect(clip_in_layer));
-    new_pass_content_bounds =
-        transform_to_target.MapRect(new_pass_content_bounds);
-    // Clip to max texture size.
-    int max_texture_size = frame_sink_->GetMaxTextureSize();
-    new_pass_content_bounds.set_width(
-        std::min(new_pass_content_bounds.width(), max_texture_size));
-    new_pass_content_bounds.set_height(
-        std::min(new_pass_content_bounds.height(), max_texture_size));
-
+    new_pass_clip = gfx::ToEnclosedRect(clip_in_layer);
+    if (layer.masks_to_bounds()) {
+      new_pass_clip.Intersect(gfx::Rect(layer.bounds()));
+    }
+    new_pass_clip = transform_to_target.MapRect(new_pass_clip);
     new_pass = viz::CompositorRenderPass::Create();
-    // Note output_rect and damage_rect are updated below.
+    // Note output_rect and damage_rect are further updated below.
     viz::CompositorRenderPassId new_pass_id(layer.id());
-    new_pass->SetNew(new_pass_id, /*output_rect=*/new_pass_content_bounds,
-                     /*damage_rect=*/new_pass_content_bounds,
-                     new_pass_transform_to_root);
+    new_pass->SetNew(new_pass_id, /*output_rect=*/new_pass_clip,
+                     /*damage_rect=*/new_pass_clip, new_pass_transform_to_root);
   }
 
   // If a new pass is created, then there is no target clip when drawing into
@@ -650,22 +644,6 @@
   }
   viz::SharedQuadState* shared_quad_state =
       parent_pass.CreateAndAppendSharedQuadState();
-  // Any clip introduced by this layer is already applied by the bounds of the
-  // new pass, so only need to apply any clips in parents target that came
-  // from parent.
-  absl::optional<gfx::Rect> clip_opt;
-  if (parent_clip_in_target) {
-    clip_opt = gfx::ToEnclosingRect(*parent_clip_in_target);
-  }
-  const bool new_pass_contents_opaque =
-      occlusion_in_new_pass.Contains(new_pass_content_bounds);
-  shared_quad_state->SetAll(
-      transform_new_pass_to_parent_target, new_pass_content_bounds,
-      new_pass_content_bounds, gfx::MaskFilterInfo(), clip_opt,
-      new_pass_contents_opaque, parent_opacity * layer.opacity(),
-      SkBlendMode::kSrcOver, 0);
-  auto* quad =
-      parent_pass.CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>();
 
   // Union through quad list in new pass to compute content rect.
   gfx::Rect content_rect;
@@ -674,7 +652,27 @@
         new_pass_quad->shared_quad_state->quad_to_target_transform.MapRect(
             new_pass_quad->rect));
   }
-  content_rect.Intersect(new_pass_content_bounds);
+  content_rect.Intersect(new_pass_clip);
+  // Clip to max texture size.
+  int max_texture_size = frame_sink_->GetMaxTextureSize();
+  content_rect.set_width(std::min(content_rect.width(), max_texture_size));
+  content_rect.set_height(std::min(content_rect.height(), max_texture_size));
+
+  // Any clip introduced by this layer is already applied by the bounds of the
+  // new pass, so only need to apply any clips in parents target that came
+  // from parent.
+  absl::optional<gfx::Rect> clip_opt;
+  if (parent_clip_in_target) {
+    clip_opt = gfx::ToEnclosingRect(*parent_clip_in_target);
+  }
+  const bool new_pass_contents_opaque =
+      occlusion_in_new_pass.Contains(content_rect);
+  shared_quad_state->SetAll(
+      transform_new_pass_to_parent_target, content_rect, content_rect,
+      gfx::MaskFilterInfo(), clip_opt, new_pass_contents_opaque,
+      parent_opacity * layer.opacity(), SkBlendMode::kSrcOver, 0);
+  auto* quad =
+      parent_pass.CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>();
 
   gfx::RectF tex_coord_rect(gfx::Rect(content_rect.size()));
   quad->SetAll(shared_quad_state, content_rect, content_rect,
@@ -692,6 +690,7 @@
   // `has_damage_from_contributing_content`.
   new_pass->output_rect = content_rect;
   new_pass->damage_rect = content_rect;
+  new_pass->filters = layer.GetFilters();
   data.frame.render_pass_list.push_back(std::move(new_pass));
 }
 
diff --git a/cc/slim/slim_layer_tree_compositor_frame_unittest.cc b/cc/slim/slim_layer_tree_compositor_frame_unittest.cc
index 996acb97..8ce6c0362 100644
--- a/cc/slim/slim_layer_tree_compositor_frame_unittest.cc
+++ b/cc/slim/slim_layer_tree_compositor_frame_unittest.cc
@@ -1168,8 +1168,9 @@
     auto* render_pass_quad = viz::CompositorRenderPassDrawQuad::MaterialCast(
         root_pass->quad_list.ElementAt(0));
     auto* shared_quad_state = render_pass_quad->shared_quad_state;
-    EXPECT_EQ(shared_quad_state->quad_layer_rect, gfx::Rect(50, 50));
-    EXPECT_EQ(shared_quad_state->visible_quad_layer_rect, gfx::Rect(50, 50));
+    EXPECT_EQ(shared_quad_state->quad_layer_rect, gfx::Rect(20, 20, 30, 30));
+    EXPECT_EQ(shared_quad_state->visible_quad_layer_rect,
+              gfx::Rect(20, 20, 30, 30));
     EXPECT_EQ(shared_quad_state->clip_rect, absl::nullopt);
   }
 }
@@ -1239,6 +1240,71 @@
   }
 }
 
+TEST_F(SlimLayerTreeCompositorFrameTest, FiltersOnNonDrawingLayer) {
+  auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray);
+  layer_tree_->SetRoot(root_layer);
+
+  auto filter_layer = cc::slim::Layer::Create();
+  filter_layer->SetFilters({cc::slim::Filter::CreateBrightness(0.5f)});
+  auto solid_color_layer =
+      CreateSolidColorLayer(gfx::Size(50, 50), SkColors::kRed);
+  filter_layer->AddChild(solid_color_layer);
+  root_layer->AddChild(filter_layer);
+
+  {
+    viz::CompositorFrame frame = ProduceFrame();
+    ASSERT_EQ(frame.render_pass_list.size(), 2u);
+    auto& child_pass = frame.render_pass_list.front();
+    ASSERT_THAT(child_pass->quad_list,
+                ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kRed),
+                                  viz::HasRect(gfx::Rect(50, 50)),
+                                  viz::HasVisibleRect(gfx::Rect(50, 50)))));
+    EXPECT_EQ(child_pass->output_rect, gfx::Rect(50, 50));
+    EXPECT_THAT(child_pass->filters.operations(),
+                ElementsAre(cc::FilterOperation::CreateBrightnessFilter(0.5f)));
+    auto& root_pass = frame.render_pass_list.back();
+    ASSERT_THAT(
+        root_pass->quad_list,
+        ElementsAre(AllOf(viz::IsCompositorRenderPassQuad(child_pass->id),
+                          viz::HasRect(gfx::Rect(50, 50)),
+                          viz::HasVisibleRect(gfx::Rect(50, 50))),
+                    viz::IsSolidColorQuad(SkColors::kGray)));
+  }
+
+  // Clip the child pass.
+  filter_layer->SetBounds(gfx::Size(25, 25));
+  filter_layer->SetMasksToBounds(true);
+  {
+    viz::CompositorFrame frame = ProduceFrame();
+    ASSERT_EQ(frame.render_pass_list.size(), 2u);
+    auto& child_pass = frame.render_pass_list.front();
+    ASSERT_THAT(child_pass->quad_list,
+                ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kRed),
+                                  viz::HasRect(gfx::Rect(50, 50)),
+                                  viz::HasVisibleRect(gfx::Rect(25, 25)))));
+    EXPECT_EQ(child_pass->output_rect, gfx::Rect(25, 25));
+    EXPECT_THAT(child_pass->filters.operations(),
+                ElementsAre(cc::FilterOperation::CreateBrightnessFilter(0.5f)));
+    auto& root_pass = frame.render_pass_list.back();
+    ASSERT_THAT(
+        root_pass->quad_list,
+        ElementsAre(AllOf(viz::IsCompositorRenderPassQuad(child_pass->id),
+                          viz::HasRect(gfx::Rect(25, 25)),
+                          viz::HasVisibleRect(gfx::Rect(25, 25))),
+                    viz::IsSolidColorQuad(SkColors::kGray)));
+  }
+
+  // Completely clip the child pass.
+  filter_layer->SetBounds(gfx::Size(0, 0));
+  {
+    viz::CompositorFrame frame = ProduceFrame();
+    ASSERT_EQ(frame.render_pass_list.size(), 1u);
+    auto& root_pass = frame.render_pass_list.back();
+    ASSERT_THAT(root_pass->quad_list,
+                ElementsAre(viz::IsSolidColorQuad(SkColors::kGray)));
+  }
+}
+
 TEST_F(SlimLayerTreeCompositorFrameTest, Opacity) {
   auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray);
   layer_tree_->SetRoot(root_layer);
@@ -1443,17 +1509,20 @@
     ASSERT_THAT(
         root_pass->quad_list,
         ElementsAre(AllOf(viz::IsCompositorRenderPassQuad(child_pass->id),
-                          // RenderPassQuad is not fully covered.
-                          viz::AreContentsOpaque(false)),
+                          // Pass rects shrinks to content rect.
+                          viz::HasRect(gfx::Rect(100, 25)),
+                          viz::HasVisibleRect(gfx::Rect(100, 25)),
+                          viz::AreContentsOpaque(true)),
                     AllOf(viz::IsSolidColorQuad(SkColors::kGray),
                           viz::HasRect(viewport_),
                           // Occluded by child pass.
                           viz::HasVisibleRect(gfx::Rect(0, 25, 100, 75)))));
   }
 
-  // Add another layer to fully cover the child quad.
+  // Add another layer so that the bottom right corner of the render pass is not
+  // covered.
   auto child_pass_layer_2 =
-      CreateSolidColorLayer(gfx::Size(200, 50), SkColors::kBlue);
+      CreateSolidColorLayer(gfx::Size(100, 50), SkColors::kGreen);
   child_pass_layer_2->SetPosition(gfx::PointF(0.0f, 50.0f));
   child_pass_root->AddChild(child_pass_layer_2);
   {
@@ -1461,9 +1530,9 @@
     ASSERT_EQ(frame.render_pass_list.size(), 2u);
     auto& child_pass = frame.render_pass_list.front();
     ASSERT_THAT(child_pass->quad_list,
-                ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kBlue),
-                                  viz::HasRect(gfx::Rect(200, 50)),
-                                  viz::HasVisibleRect(gfx::Rect(200, 50))),
+                ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kGreen),
+                                  viz::HasRect(gfx::Rect(100, 50)),
+                                  viz::HasVisibleRect(gfx::Rect(100, 50))),
                             AllOf(viz::IsSolidColorQuad(SkColors::kRed),
                                   viz::HasRect(gfx::Rect(200, 50)),
                                   viz::HasVisibleRect(gfx::Rect(200, 50)))));
@@ -1472,6 +1541,42 @@
     ASSERT_THAT(
         root_pass->quad_list,
         ElementsAre(AllOf(viz::IsCompositorRenderPassQuad(child_pass->id),
+                          viz::HasRect(gfx::Rect(100, 50)),
+                          viz::HasVisibleRect(gfx::Rect(100, 50)),
+                          // RenderPassQuad is fully covered.
+                          viz::AreContentsOpaque(false)),
+                    AllOf(viz::IsSolidColorQuad(SkColors::kGray),
+                          viz::HasRect(viewport_),
+                          // Occluded by child pass.
+                          viz::HasVisibleRect(gfx::Rect(0, 25, 100, 75)))));
+  }
+
+  // Add another layer to fully cover the child pass.
+  auto child_pass_layer_3 =
+      CreateSolidColorLayer(gfx::Size(100, 50), SkColors::kBlue);
+  child_pass_layer_3->SetPosition(gfx::PointF(100.0f, 50.0f));
+  child_pass_root->AddChild(child_pass_layer_3);
+  {
+    viz::CompositorFrame frame = ProduceFrame();
+    ASSERT_EQ(frame.render_pass_list.size(), 2u);
+    auto& child_pass = frame.render_pass_list.front();
+    ASSERT_THAT(child_pass->quad_list,
+                ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kBlue),
+                                  viz::HasRect(gfx::Rect(100, 50)),
+                                  viz::HasVisibleRect(gfx::Rect(100, 50))),
+                            AllOf(viz::IsSolidColorQuad(SkColors::kGreen),
+                                  viz::HasRect(gfx::Rect(100, 50)),
+                                  viz::HasVisibleRect(gfx::Rect(100, 50))),
+                            AllOf(viz::IsSolidColorQuad(SkColors::kRed),
+                                  viz::HasRect(gfx::Rect(200, 50)),
+                                  viz::HasVisibleRect(gfx::Rect(200, 50)))));
+
+    auto& root_pass = frame.render_pass_list.back();
+    ASSERT_THAT(
+        root_pass->quad_list,
+        ElementsAre(AllOf(viz::IsCompositorRenderPassQuad(child_pass->id),
+                          viz::HasRect(gfx::Rect(100, 50)),
+                          viz::HasVisibleRect(gfx::Rect(100, 50)),
                           // RenderPassQuad is fully covered.
                           viz::AreContentsOpaque(true)),
                     AllOf(viz::IsSolidColorQuad(SkColors::kGray),
@@ -1480,7 +1585,7 @@
                           viz::HasVisibleRect(gfx::Rect(0, 50, 100, 50)))));
   }
 
-  // Expand child layer so it's partially occluded by child layer 2.
+  // Expand child layer so it's partially occluded by child layer 2 and 3.
   child_pass_layer->SetBounds(gfx::Size(200, 100));
   {
     viz::CompositorFrame frame = ProduceFrame();
@@ -1488,8 +1593,11 @@
     auto& child_pass = frame.render_pass_list.front();
     ASSERT_THAT(child_pass->quad_list,
                 ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kBlue),
-                                  viz::HasRect(gfx::Rect(200, 50)),
-                                  viz::HasVisibleRect(gfx::Rect(200, 50))),
+                                  viz::HasRect(gfx::Rect(100, 50)),
+                                  viz::HasVisibleRect(gfx::Rect(100, 50))),
+                            AllOf(viz::IsSolidColorQuad(SkColors::kGreen),
+                                  viz::HasRect(gfx::Rect(100, 50)),
+                                  viz::HasVisibleRect(gfx::Rect(100, 50))),
                             AllOf(viz::IsSolidColorQuad(SkColors::kRed),
                                   viz::HasRect(gfx::Rect(200, 100)),
                                   // Partially occluded.
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 48cfd34..6d09bd0 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -468,10 +468,10 @@
   "java/res/layout/autofill_server_data_text_label.xml",
   "java/res/layout/autofill_update_address_profile_prompt.xml",
   "java/res/layout/bookmark_edit.xml",
-  "java/res/layout/bookmark_folder_row.xml",
   "java/res/layout/bookmark_folder_select_activity.xml",
-  "java/res/layout/bookmark_item_row.xml",
   "java/res/layout/bookmark_main.xml",
+  "java/res/layout/bookmark_row_layout.xml",
+  "java/res/layout/bookmark_row_layout_visual.xml",
   "java/res/layout/bookmark_save_flow.xml",
   "java/res/layout/bookmark_section_header.xml",
   "java/res/layout/bookmark_toolbar.xml",
@@ -561,7 +561,6 @@
   "java/res/layout/password_no_result.xml",
   "java/res/layout/passwords_error_dialog.xml",
   "java/res/layout/passwords_progress_dialog.xml",
-  "java/res/layout/power_bookmark_shopping_item_row.xml",
   "java/res/layout/power_bookmark_tag_chip_list.xml",
   "java/res/layout/powered_by_chrome_footer.xml",
   "java/res/layout/radio_button_group_homepage_preference.xml",
diff --git a/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayout.java b/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayout.java
index c7dafce..fc24112 100644
--- a/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayout.java
+++ b/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayout.java
@@ -897,6 +897,11 @@
         return super.canHostBeFocusable();
     }
 
+    @Override
+    public boolean isRunningAnimations() {
+        return mDeferredAnimationRunnable != null || mTabToSwitcherAnimation != null;
+    }
+
     /**
      * Shrink/Expand animation is disabled for Tablet TabSwitcher launch polish.
      * @return Whether shrink/expand animation is enabled.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
index 64dece91..e3cd69d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
@@ -13,7 +13,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.chromium.base.SysUtils;
 import org.chromium.base.jank_tracker.JankTracker;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
@@ -82,8 +81,7 @@
                 tabContentManager, browserControlsStateProvider, tabCreatorManager,
                 menuOrKeyboardActionController, containerView, multiWindowModeStateDispatcher,
                 scrimCoordinator,
-                TabUiFeatureUtilities.isTabGroupsAndroidContinuationEnabled(activity)
-                                && SysUtils.isLowEndDevice()
+                TabUiFeatureUtilities.shouldUseListMode(activity)
                         ? TabListCoordinator.TabListMode.LIST
                         : TabListCoordinator.TabListMode.GRID,
                 rootView, dynamicResourceLoaderSupplier, snackbarManager, modalDialogManager,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayout.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayout.java
index dd15d7d..5eff80bf 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayout.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayout.java
@@ -728,6 +728,11 @@
         return super.canHostBeFocusable();
     }
 
+    @Override
+    public boolean isRunningAnimations() {
+        return mDeferredAnimationRunnable != null || mTabToSwitcherAnimation != null;
+    }
+
     /**
      * Shrink/Expand animation is disabled for Tablet TabSwitcher launch polish.
      * @return Whether shrink/expand animation is enabled.
diff --git a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java
index 1c141be8..79058636 100644
--- a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java
+++ b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java
@@ -33,8 +33,10 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.app.ChromeActivity;
+import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.webapps.WebappActivity;
@@ -383,6 +385,12 @@
         // Hide system UI.
         VrModuleProvider.getDelegate().setSystemUiVisibilityForVr(mActivity);
 
+        Supplier<CompositorViewHolder> compositorViewHolderSupplier =
+                mActivity.getCompositorViewHolderSupplier();
+        if (compositorViewHolderSupplier.hasValue()) {
+            compositorViewHolderSupplier.get().getCompositorView().setOverlayVrMode(true);
+        }
+
         // Set correct orientation.
         if (mRestoreOrientation == null) {
             mRestoreOrientation = mActivity.getRequestedOrientation();
@@ -409,7 +417,11 @@
                     flags & ~VrDelegate.VR_SYSTEM_UI_FLAGS);
         }
         mRestoreSystemUiVisibility = false;
-
+        Supplier<CompositorViewHolder> compositorViewHolderSupplier =
+                mActivity.getCompositorViewHolderSupplier();
+        if (compositorViewHolderSupplier.hasValue()) {
+            compositorViewHolderSupplier.get().getCompositorView().setOverlayVrMode(false);
+        }
         mActivity.getWindow().getAttributes().rotationAnimation =
                 WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
     }
diff --git a/chrome/android/java/res/layout/autofill_card_unmask_prompt_card_details.xml b/chrome/android/java/res/layout/autofill_card_unmask_prompt_card_details.xml
index 55dc07c..71ed01da 100644
--- a/chrome/android/java/res/layout/autofill_card_unmask_prompt_card_details.xml
+++ b/chrome/android/java/res/layout/autofill_card_unmask_prompt_card_details.xml
@@ -12,8 +12,8 @@
 
     <org.chromium.ui.widget.ChromeImageView
         android:id="@+id/card_icon"
-        android:layout_width="32dp"
-        android:layout_height="20dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_alignParentStart="true"
         android:layout_centerVertical="true"
         android:layout_marginEnd="16dp"
diff --git a/chrome/android/java/res/layout/bookmark_folder_row.xml b/chrome/android/java/res/layout/bookmark_folder_row.xml
deleted file mode 100644
index 196cb4a3..0000000
--- a/chrome/android/java/res/layout/bookmark_folder_row.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright 2015 The Chromium Authors
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
--->
-
-<view class="org.chromium.chrome.browser.bookmarks.BookmarkFolderRow"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content" />
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/bookmark_item_row.xml b/chrome/android/java/res/layout/bookmark_item_row.xml
deleted file mode 100644
index 9e3109d6..0000000
--- a/chrome/android/java/res/layout/bookmark_item_row.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright 2015 The Chromium Authors
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
--->
-
-<view class="org.chromium.chrome.browser.bookmarks.BookmarkItemRow"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/bookmark_item_row"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content" />
diff --git a/chrome/android/java/res/layout/bookmark_row_layout.xml b/chrome/android/java/res/layout/bookmark_row_layout.xml
new file mode 100644
index 0000000..2f20503
--- /dev/null
+++ b/chrome/android/java/res/layout/bookmark_row_layout.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2023 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/content"
+    style="@style/ListItemContainer">
+
+    <ImageView
+      android:id="@+id/start_icon"
+      android:background="@drawable/list_item_icon_modern_bg"
+      style="@style/ListItemStartIcon"
+      android:importantForAccessibility="no" />
+
+    <!-- Common title/description shared between list item views. -->
+    <include layout="@layout/title_and_description_layout" />
+
+    <org.chromium.ui.widget.ChromeImageButton
+        android:id="@+id/end_icon"
+        android:visibility="gone"
+        style="@style/ListItemEndIcon" />
+
+    <!-- List menu button used for contextual options. -->
+    <include layout="@layout/list_menu_button" />
+
+    <!-- TODO(crbug.com/1424835): Move drag handle to a vector drawable. -->
+    <!-- TODO(crbug.com/1424837): Support dynamic colors for drag handle. -->
+    <org.chromium.ui.widget.ChromeImageButton
+        android:id="@+id/drag_handle"
+        android:visibility="gone"
+        android:src="@drawable/ic_drag_handle_grey600_24dp"
+        android:tint="@color/default_icon_color_tint_list"
+        style="@style/ListItemEndIcon" />
+</LinearLayout>
\ No newline at end of file
diff --git a/components/browser_ui/widget/android/java/res/layout/modern_list_item_view_v2.xml b/chrome/android/java/res/layout/bookmark_row_layout_visual.xml
similarity index 64%
rename from components/browser_ui/widget/android/java/res/layout/modern_list_item_view_v2.xml
rename to chrome/android/java/res/layout/bookmark_row_layout_visual.xml
index 4002ccd..217c0c0a 100644
--- a/components/browser_ui/widget/android/java/res/layout/modern_list_item_view_v2.xml
+++ b/chrome/android/java/res/layout/bookmark_row_layout_visual.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright 2021 The Chromium Authors
+Copyright 2023 The Chromium Authors
 Use of this source code is governed by a BSD-style license that can be
 found in the LICENSE file.
 -->
@@ -13,22 +13,25 @@
 
     <ImageView
       android:id="@+id/start_icon"
+      android:background="@drawable/list_item_icon_modern_bg_rect"
       style="@style/ListItemStartIconV2"
       android:importantForAccessibility="no" />
 
     <!-- Common title/description shared between list item views. -->
     <include layout="@layout/title_and_description_layout" />
 
-    <!-- TODO(crbug.com/1241509): Allow an arbitrary amount of buttons to be added here. -->
     <org.chromium.ui.widget.ChromeImageButton
-        android:id="@+id/optional_button"
+        android:id="@+id/end_icon"
         android:visibility="gone"
         style="@style/ListItemEndIconV2" />
 
-    <org.chromium.ui.widget.ChromeImageButton
-        android:id="@+id/end_button"
-        android:visibility="gone"
-        style="@style/ListItemEndIconV2"
-        android:layout_marginEnd="4dp" />
+    <!-- List menu button used for contextual options. -->
+    <include layout="@layout/list_menu_button" />
 
+    <org.chromium.ui.widget.ChromeImageButton
+        android:id="@+id/drag_handle"
+        android:visibility="gone"
+        android:src="@drawable/ic_drag_handle_grey600_24dp"
+        android:tint="@color/default_icon_color_tint_list"
+        style="@style/ListItemEndIconV2" />
 </LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/compact_price_drop_view.xml b/chrome/android/java/res/layout/compact_price_drop_view.xml
index e0d0bc9..0c22d69b 100644
--- a/chrome/android/java/res/layout/compact_price_drop_view.xml
+++ b/chrome/android/java/res/layout/compact_price_drop_view.xml
@@ -11,13 +11,18 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content">
     <TextView
-        android:id="@+id/primary_text"
+        android:id="@+id/normal_price_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
+    <TextView
+        android:id="@+id/price_drop_primary_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
         android:layout_marginEnd="8dp" />
     <TextView
-        android:id="@+id/secondary_text"
+        android:id="@+id/price_drop_secondary_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
diff --git a/chrome/android/java/res/layout/power_bookmark_shopping_item_row.xml b/chrome/android/java/res/layout/power_bookmark_shopping_item_row.xml
deleted file mode 100644
index 4fbff78..0000000
--- a/chrome/android/java/res/layout/power_bookmark_shopping_item_row.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright 2021 The Chromium Authors
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
--->
-
-<view class="org.chromium.chrome.browser.bookmarks.PowerBookmarkShoppingItemRow"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/power_bookmark_shopping_row"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content" />
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 928b987..2ca7427 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -399,8 +399,4 @@
 
     <!-- Price Drop dimensions -->
     <dimen name="price_drop_spotted_iph_ntp_tabswitcher_y_inset">4dp</dimen>
-
-    <!-- Card Unmask dialog -->
-    <dimen name="card_unmask_dialog_credit_card_icon_width">32dp</dimen>
-    <dimen name="card_unmask_dialog_credit_card_icon_height">20dp</dimen>
 </resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java
index 89b585f..8f7aa7c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java
@@ -28,6 +28,7 @@
 import org.chromium.base.IntentUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.SynchronousInitializationActivity;
+import org.chromium.chrome.browser.bookmarks.BookmarkFolderRow;
 import org.chromium.chrome.browser.bookmarks.BookmarkModel;
 import org.chromium.chrome.browser.bookmarks.BookmarkModelObserver;
 import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
@@ -36,7 +37,6 @@
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkItem;
 import org.chromium.components.browser_ui.util.TraceEventVectorDrawableCompat;
-import org.chromium.components.browser_ui.widget.selectable_list.SelectableItemView;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -428,7 +428,7 @@
                 iconDrawable = vectorDrawable;
             }
 
-            SelectableItemView.applyModernIconStyle(startIcon, iconDrawable, entry.mIsSelected);
+            BookmarkFolderRow.applyModernIconStyle(startIcon, iconDrawable, entry.mIsSelected);
         }
 
         /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/omnibox/ActionChipsDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/omnibox/ActionChipsDelegateImpl.java
index 47749af..dd19522 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/omnibox/ActionChipsDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/omnibox/ActionChipsDelegateImpl.java
@@ -61,15 +61,9 @@
         mModalDialogManagerSupplier = modalDialogManagerSupplier;
     }
 
-    @Override
-    public void execute(OmniboxPedal omniboxPedal) {
-        if (omniboxPedal.hasActionId()) {
-            executeNonPedalAction(omniboxPedal);
-            return;
-        }
-
+    private void executePedalAction(OmniboxPedal omniboxPedal) {
         @OmniboxPedalType
-        int omniboxPedalType = omniboxPedal.getPedalID();
+        int omniboxPedalType = omniboxPedal.getPedalId();
         SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
         switch (omniboxPedalType) {
             case OmniboxPedalType.CLEAR_BROWSING_DATA:
@@ -139,8 +133,13 @@
         return;
     }
 
-    private void executeNonPedalAction(OmniboxPedal omniboxPedal) {
-        switch (omniboxPedal.getActionID()) {
+    @Override
+    public void execute(OmniboxPedal omniboxPedal) {
+        switch (omniboxPedal.getActionId()) {
+            case OmniboxActionType.PEDAL:
+                executePedalAction(omniboxPedal);
+                break;
+
             case OmniboxActionType.HISTORY_CLUSTERS:
                 if (mHistoryClustersCoordinator != null) {
                     assert omniboxPedal instanceof HistoryClustersAction;
@@ -186,12 +185,12 @@
 
     @Override
     public @NonNull ChipIcon getIcon(OmniboxPedal omniboxPedal) {
-        if (!omniboxPedal.hasPedalId()) {
+        if (omniboxPedal.getActionId() != OmniboxActionType.PEDAL) {
             return getActionIcon(omniboxPedal);
         }
 
         @OmniboxPedalType
-        int omniboxPedalType = omniboxPedal.getPedalID();
+        int omniboxPedalType = omniboxPedal.getPedalId();
 
         switch (omniboxPedalType) {
             case OmniboxPedalType.CLEAR_BROWSING_DATA:
@@ -218,7 +217,7 @@
 
     /** Returns the icon for an action that's not a pedal. */
     private ChipIcon getActionIcon(OmniboxPedal omniboxPedal) {
-        int omniboxActionType = omniboxPedal.getActionID();
+        int omniboxActionType = omniboxPedal.getActionId();
 
         switch (omniboxActionType) {
             case OmniboxActionType.HISTORY_CLUSTERS:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index 4364b16..4393556 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -165,8 +165,7 @@
             // Populate card details.
             ChromeImageView cardIconView = (ChromeImageView) mMainView.findViewById(R.id.card_icon);
             cardIconView.setImageDrawable(AutofillUiUtils.getCardIcon(context, cardArtUrl,
-                    cardIconId, R.dimen.card_unmask_dialog_credit_card_icon_width,
-                    R.dimen.card_unmask_dialog_credit_card_icon_height,
+                    cardIconId, getCardIconWidthId(), getCardIconHeightId(),
                     isVirtualCard
                             || ChromeFeatureList.isEnabled(
                                     ChromeFeatureList.AUTOFILL_ENABLE_CARD_ART_IMAGE)));
@@ -604,4 +603,20 @@
     public String getErrorMessage() {
         return mErrorMessage.getText().toString();
     }
+
+    public static int getCardIconWidthId() {
+        if (ChromeFeatureList.isEnabled(
+                    ChromeFeatureList.AUTOFILL_ENABLE_NEW_CARD_ART_AND_NETWORK_IMAGES)) {
+            return R.dimen.card_unmask_dialog_credit_card_icon_width_new;
+        }
+        return R.dimen.card_unmask_dialog_credit_card_icon_width;
+    }
+
+    public static int getCardIconHeightId() {
+        if (ChromeFeatureList.isEnabled(
+                    ChromeFeatureList.AUTOFILL_ENABLE_NEW_CARD_ART_AND_NETWORK_IMAGES)) {
+            return R.dimen.card_unmask_dialog_credit_card_icon_height_new;
+        }
+        return R.dimen.card_unmask_dialog_credit_card_icon_height;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderRow.java
index 22a2b044..a0049231 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderRow.java
@@ -6,7 +6,9 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.widget.ImageView;
 
 import androidx.appcompat.content.res.AppCompatResources;
 
@@ -14,23 +16,29 @@
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkItem;
 import org.chromium.components.bookmarks.BookmarkType;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
+import org.chromium.components.browser_ui.widget.TintedDrawable;
 
 /**
  * A row view that shows folder info in the bookmarks UI.
  */
 public class BookmarkFolderRow extends BookmarkRow {
-
     /**
-     * Constructor for inflating from XML.
+     * Factory constructor for building the view programmatically.
+     * @param context The calling context, usually the parent view.
+     * @param isVisualRefreshEnabled Whether to show the visual or compact bookmark row.
      */
-    public BookmarkFolderRow(Context context, AttributeSet attrs) {
-        super(context, attrs);
+    public static BookmarkFolderRow buildView(Context context, boolean isVisualRefreshEnabled) {
+        BookmarkFolderRow row = new BookmarkFolderRow(context, null);
+        BookmarkRow.buildView(row, context, isVisualRefreshEnabled);
+        return row;
     }
 
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        setStartIconDrawable(BookmarkUtils.getFolderIcon(getContext(), BookmarkType.NORMAL));
+    /** Constructor for inflating from XML. */
+    public BookmarkFolderRow(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        setIconDrawable(BookmarkUtils.getFolderIcon(getContext(), BookmarkType.NORMAL));
     }
 
     // BookmarkRow implementation.
@@ -62,15 +70,41 @@
                             : getResources().getString(R.string.no_bookmarks));
         }
 
-        setStartIconDrawable(BookmarkUtils.getFolderIcon(getContext(), item.getId().getType()));
+        setIconDrawable(BookmarkUtils.getFolderIcon(getContext(), item.getId().getType()));
         return item;
     }
 
+    // CheckableSelectableItemView implementation.
+
     @Override
-    protected ColorStateList getDefaultStartIconTint() {
+    protected ColorStateList getDefaultIconTint() {
         @BookmarkType
         int type = (mBookmarkId == null) ? BookmarkType.NORMAL : mBookmarkId.getType();
         return AppCompatResources.getColorStateList(
                 getContext(), BookmarkUtils.getFolderIconTint(type));
     }
+
+    /**
+     * Sets the icon for the image view: the default icon if unselected, the check mark if selected.
+     *
+     * @param imageView     The image view in which the icon will be presented.
+     * @param defaultIcon   The default icon that will be displayed if not selected.
+     * @param isSelected    Whether the item is selected or not.
+     */
+    public static void applyModernIconStyle(
+            ImageView imageView, Drawable defaultIcon, boolean isSelected) {
+        imageView.setBackgroundResource(R.drawable.list_item_icon_modern_bg);
+        Drawable drawable;
+        if (isSelected) {
+            drawable = TintedDrawable.constructTintedDrawable(
+                    imageView.getContext(), R.drawable.ic_check_googblue_24dp);
+            drawable.setTint(SemanticColorUtils.getDefaultIconColorInverse(imageView.getContext()));
+        } else {
+            drawable = defaultIcon;
+        }
+        imageView.setImageDrawable(drawable);
+        imageView.getBackground().setLevel(isSelected
+                        ? imageView.getResources().getInteger(R.integer.list_item_level_selected)
+                        : imageView.getResources().getInteger(R.integer.list_item_level_default));
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java
index ce9ad5c..5a70e9d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java
@@ -22,6 +22,7 @@
 /**
  * A row view that shows bookmark info in the bookmarks UI.
  */
+// TODO (crbug.com/1424431): Make this class more extensible.
 public class BookmarkItemRow extends BookmarkRow implements LargeIconCallback {
     private GURL mUrl;
     private RoundedIconGenerator mIconGenerator;
@@ -30,19 +31,30 @@
     private boolean mFaviconCancelled;
 
     /**
-     * Constructor for inflating from XML.
+     * Factory constructor for building the view programmatically.
+     * @param context The calling context, usually the parent view.
+     * @param isVisualRefreshEnabled Whether to show the visual or compact bookmark row.
      */
+    public static BookmarkItemRow buildView(Context context, boolean isVisualRefreshEnabled) {
+        BookmarkItemRow row = new BookmarkItemRow(context, null);
+        BookmarkRow.buildView(row, context, isVisualRefreshEnabled);
+        return row;
+    }
+
+    /** Constructor for inflating from XML. */
     public BookmarkItemRow(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mMinIconSize = isVisualRefreshEnabled()
+
+        boolean isVisualRefreshEnabled = BookmarkFeatures.isBookmarksVisualRefreshEnabled();
+        mMinIconSize = isVisualRefreshEnabled
                 ? getResources().getDimensionPixelSize(R.dimen.bookmark_refresh_min_start_icon_size)
                 : getResources().getDimensionPixelSize(R.dimen.default_favicon_min_size);
 
-        mDisplayedIconSize = isVisualRefreshEnabled()
+        mDisplayedIconSize = isVisualRefreshEnabled
                 ? getResources().getDimensionPixelSize(
                         R.dimen.bookmark_refresh_preferred_start_icon_size)
                 : getResources().getDimensionPixelSize(R.dimen.default_favicon_size);
-        if (isVisualRefreshEnabled()) {
+        if (isVisualRefreshEnabled) {
             mIconGenerator = new RoundedIconGenerator(mDisplayedIconSize, mDisplayedIconSize,
                     mDisplayedIconSize / 2,
                     getContext().getColor(R.color.default_favicon_background_color),
@@ -98,7 +110,7 @@
         if (mFaviconCancelled) return;
         Drawable iconDrawable = FaviconUtils.getIconDrawableWithoutFilter(
                 icon, mUrl, fallbackColor, mIconGenerator, getResources(), mDisplayedIconSize);
-        setStartIconDrawable(iconDrawable);
+        setIconDrawable(iconDrawable);
     }
 
     protected boolean getFaviconCancelledForTesting() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
index e171a57..4b39ea7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
@@ -13,6 +13,7 @@
 
 import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.RecyclerView;
 
 import org.chromium.base.ContextUtils;
@@ -247,11 +248,19 @@
             case ViewType.SECTION_HEADER:
                 return buildSectionHeaderView(parent);
             case ViewType.FOLDER:
-                return buildBookmarkFolderView(parent);
+                BookmarkFolderRow folderRow = buildBookmarkFolderView(parent);
+                folderRow.onDelegateInitialized(mMediator);
+                return folderRow;
             case ViewType.BOOKMARK:
-                return buildBookmarkItemView(parent);
+                BookmarkItemRow itemRow = buildBookmarkItemView(parent);
+                itemRow.onDelegateInitialized(mMediator);
+                return itemRow;
             case ViewType.SHOPPING_POWER_BOOKMARK:
-                return buildShoppingItemView(parent);
+                PowerBookmarkShoppingItemRow shoppingItemRow = buildShoppingItemView(parent);
+                shoppingItemRow.onDelegateInitialized(mMediator);
+                // TODO(https://crbug.com/1416611): Move init to view binding.
+                shoppingItemRow.init(mImageFetcher, mBookmarkModel, mSnackbarManager, mProfile);
+                return shoppingItemRow;
             case ViewType.DIVIDER:
                 return buildDividerView(parent);
             case ViewType.SHOPPING_FILTER:
@@ -308,20 +317,22 @@
         return inflate(parent, org.chromium.chrome.R.layout.bookmark_section_header);
     }
 
-    private View buildBookmarkFolderView(ViewGroup parent) {
-        return inflateBookmarkRow(parent, org.chromium.chrome.R.layout.bookmark_folder_row);
+    @VisibleForTesting
+    static BookmarkFolderRow buildBookmarkFolderView(ViewGroup parent) {
+        return BookmarkFolderRow.buildView(
+                parent.getContext(), BookmarkFeatures.isBookmarksVisualRefreshEnabled());
     }
 
-    private View buildBookmarkItemView(ViewGroup parent) {
-        return inflateBookmarkRow(parent, org.chromium.chrome.R.layout.bookmark_item_row);
+    @VisibleForTesting
+    static BookmarkItemRow buildBookmarkItemView(ViewGroup parent) {
+        return BookmarkItemRow.buildView(
+                parent.getContext(), BookmarkFeatures.isBookmarksVisualRefreshEnabled());
     }
 
-    private View buildShoppingItemView(ViewGroup parent) {
-        PowerBookmarkShoppingItemRow row = (PowerBookmarkShoppingItemRow) inflateBookmarkRow(
-                parent, org.chromium.chrome.R.layout.power_bookmark_shopping_item_row);
-        // TODO(https://crbug.com/1416611): Move init to view binding.
-        row.init(mImageFetcher, mBookmarkModel, mSnackbarManager, mProfile);
-        return row;
+    @VisibleForTesting
+    static PowerBookmarkShoppingItemRow buildShoppingItemView(ViewGroup parent) {
+        return PowerBookmarkShoppingItemRow.buildView(
+                parent.getContext(), BookmarkFeatures.isBookmarksVisualRefreshEnabled());
     }
 
     private View buildDividerView(ViewGroup parent) {
@@ -337,13 +348,6 @@
         return LayoutInflater.from(context).inflate(layoutId, parent, false);
     }
 
-    private View inflateBookmarkRow(ViewGroup parent, @LayoutRes int layoutId) {
-        BookmarkRow row = (BookmarkRow) inflate(parent, layoutId);
-        // TODO(https://crbug.com/1416611): Move onDelegateInitialized to view binding.
-        (row).onDelegateInitialized(/*delegate=*/mMediator);
-        return row;
-    }
-
     // Testing methods.
 
     public BookmarkToolbar getToolbarForTesting() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
index f58ffc35..0cdba36 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
@@ -7,15 +7,18 @@
 import static org.chromium.components.browser_ui.widget.listmenu.BasicListMenu.buildMenuListItem;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.TextView;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.content.res.AppCompatResources;
-import androidx.core.widget.ImageViewCompat;
 
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
@@ -31,7 +34,7 @@
 import org.chromium.components.browser_ui.widget.listmenu.ListMenuButton.PopupMenuShownListener;
 import org.chromium.components.browser_ui.widget.listmenu.ListMenuButtonDelegate;
 import org.chromium.components.browser_ui.widget.listmenu.ListMenuItemProperties;
-import org.chromium.components.browser_ui.widget.selectable_list.SelectableItemView;
+import org.chromium.components.browser_ui.widget.selectable_list.CheckableSelectableItemView;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectableListUtils;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 
@@ -43,15 +46,27 @@
  * Common logic for bookmark and folder rows.
  */
 public abstract class BookmarkRow
-        extends SelectableItemView<BookmarkId> implements BookmarkUiObserver {
-    protected ListMenuButton mMoreIcon;
+        extends CheckableSelectableItemView<BookmarkId> implements BookmarkUiObserver {
+    // The start icon view which is shows the favicon and the checkmark.
+    protected ImageView mStartIconView;
+    // 3-dot menu which displays contextual actions.
+    protected ListMenuButton mMoreButton;
+    // Image shown in selection mode which starts drag/drop.
     protected ImageView mDragHandle;
+    // Displays the title of the bookmark.
+    protected TextView mTitleView;
+    // Displays the url of the bookmark.
+    protected TextView mDescriptionView;
+
     protected BookmarkDelegate mDelegate;
     protected BookmarkId mBookmarkId;
+
+    /** Levels for the background. */
+    private final int mDefaultLevel;
+    private final int mSelectedLevel;
     private boolean mIsAttachedToWindow;
     private PopupMenuShownListener mPopupListener;
-    @Location
-    private int mLocation;
+    private @Location int mLocation;
     private boolean mFromFilterView;
 
     @IntDef({Location.TOP, Location.MIDDLE, Location.BOTTOM, Location.SOLO})
@@ -64,13 +79,46 @@
     }
 
     /**
-     * Constructor for inflating from XML.
+     * Factory constructor for building the view programmatically.
+     * @param row The BookmarkRow to build.
+     * @param context The calling context, usually the parent view.
+     * @param isVisualRefreshEnabled Whether to show the visual or compact bookmark row.
      */
+    protected static void buildView(
+            BookmarkRow row, Context context, boolean isVisualRefreshEnabled) {
+        row.setLayoutParams(new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        LayoutInflater.from(context).inflate(isVisualRefreshEnabled
+                        ? org.chromium.chrome.R.layout.bookmark_row_layout_visual
+                        : org.chromium.chrome.R.layout.bookmark_row_layout,
+                row);
+        row.onFinishInflate();
+    }
+
+    /** Constructor for inflating from XML. */
     public BookmarkRow(Context context, AttributeSet attrs) {
         super(context, attrs);
-        if (BookmarkFeatures.isBookmarksVisualRefreshEnabled()) {
-            enableVisualRefresh();
-        }
+
+        mDefaultLevel = getResources().getInteger(R.integer.list_item_level_default);
+        mSelectedLevel = getResources().getInteger(R.integer.list_item_level_selected);
+    }
+
+    // FrameLayout implementation.
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mStartIconView = findViewById(R.id.start_icon);
+
+        mDragHandle = findViewById(R.id.drag_handle);
+
+        mMoreButton = findViewById(R.id.more);
+        mMoreButton.setDelegate(getListMenuButtonDelegate());
+        mMoreButton.setVisibility(View.GONE);
+
+        mTitleView = findViewById(R.id.title);
+        mDescriptionView = findViewById(R.id.description);
     }
 
     /**
@@ -89,8 +137,8 @@
         mBookmarkId = bookmarkId;
         mFromFilterView = fromFilterView;
         BookmarkItem bookmarkItem = mDelegate.getModel().getBookmarkById(bookmarkId);
-        mMoreIcon.dismiss();
-        SelectableListUtils.setContentDescriptionContext(getContext(), mMoreIcon,
+        mMoreButton.dismiss();
+        SelectableListUtils.setContentDescriptionContext(getContext(), mMoreButton,
                 bookmarkItem.getTitle(), SelectableListUtils.ContentDescriptionSource.MENU_BUTTON);
 
         setChecked(isItemSelected());
@@ -119,17 +167,17 @@
 
         // If the visibility of the drag handle or more icon is not set later, it will be gone.
         mDragHandle.setVisibility(GONE);
-        mMoreIcon.setVisibility(GONE);
+        mMoreButton.setVisibility(GONE);
 
         if (mDelegate.getDragStateDelegate().getDragActive()) {
             mDragHandle.setVisibility(
                     bookmarkItem.isReorderable() && !mFromFilterView ? VISIBLE : GONE);
             mDragHandle.setEnabled(isItemSelected());
         } else {
-            mMoreIcon.setVisibility(bookmarkItem.isEditable() ? VISIBLE : GONE);
-            mMoreIcon.setClickable(!isSelectionModeActive());
-            mMoreIcon.setEnabled(mMoreIcon.isClickable());
-            mMoreIcon.setImportantForAccessibility(mMoreIcon.isClickable()
+            mMoreButton.setVisibility(bookmarkItem.isEditable() ? VISIBLE : GONE);
+            mMoreButton.setClickable(!isSelectionModeActive());
+            mMoreButton.setEnabled(mMoreButton.isClickable());
+            mMoreButton.setImportantForAccessibility(mMoreButton.isClickable()
                             ? IMPORTANT_FOR_ACCESSIBILITY_YES
                             : IMPORTANT_FOR_ACCESSIBILITY_NO);
         }
@@ -149,12 +197,12 @@
     private void initialize() {
         mDelegate.addUiObserver(this);
         mPopupListener = () -> mDelegate.onBookmarkItemMenuOpened();
-        mMoreIcon.addPopupListener(mPopupListener);
+        mMoreButton.addPopupListener(mPopupListener);
     }
 
     private void cleanup() {
-        mMoreIcon.dismiss();
-        mMoreIcon.removePopupListener(mPopupListener);
+        mMoreButton.dismiss();
+        mMoreButton.removePopupListener(mPopupListener);
         if (mDelegate != null) mDelegate.removeUiObserver(this);
     }
 
@@ -263,20 +311,6 @@
     }
 
     // FrameLayout implementation.
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        LayoutInflater.from(getContext()).inflate(R.layout.list_menu_button, mContentView);
-        mMoreIcon = findViewById(R.id.more);
-        mMoreIcon.setDelegate(getListMenuButtonDelegate());
-
-        mDragHandle = mEndButtonView;
-        mDragHandle.setImageResource(R.drawable.ic_drag_handle_grey600_24dp);
-        ImageViewCompat.setImageTintList(mDragHandle,
-                AppCompatResources.getColorStateList(
-                        getContext(), R.color.default_icon_color_tint_list));
-    }
 
     private ListMenuButtonDelegate getListMenuButtonDelegate() {
         return this::getListMenu;
@@ -298,14 +332,36 @@
         cleanup();
     }
 
-    // SelectableItem implementation.
+    // CheckableSelectableItemView implementation.
+
+    @Override
+    protected ImageView getIconView() {
+        return mStartIconView;
+    }
+
+    @Override
+    protected @Nullable ColorStateList getDefaultIconTint() {
+        return null;
+    }
+
+    @Override
+    protected int getSelectedLevel() {
+        return mSelectedLevel;
+    }
+
+    @Override
+    protected int getDefaultLevel() {
+        return mDefaultLevel;
+    }
+
+    // BookmarkUiObserver implementation.
+
     @Override
     public void onSelectionStateChange(List<BookmarkId> selectedBookmarks) {
         super.onSelectionStateChange(selectedBookmarks);
         updateVisualState();
     }
 
-    // BookmarkUiObserver implementation.
     @Override
     public void onDestroy() {
         cleanup();
@@ -321,7 +377,7 @@
         return mDelegate.getSelectionDelegate().isItemSelected(mBookmarkId);
     }
 
-    void setDragHandleOnTouchListener(OnTouchListener l) {
+    void setDragHandleOnTouchListener(View.OnTouchListener l) {
         mDragHandle.setOnTouchListener(l);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java
index 0d71a25..653d922 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java
@@ -10,12 +10,12 @@
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.bookmarks.PowerBookmarkMetrics.PriceTrackingState;
@@ -38,6 +38,15 @@
 public class PowerBookmarkShoppingItemRow extends BookmarkItemRow {
     private static final long MICRO_CURRENCY_QUOTIENT = 1000000;
 
+    // UI elements.
+    // Allows subclasses to add special views below the description (e.g. the price for shopping).
+    protected FrameLayout mCustomTextContainer;
+    private ImageView mPriceTrackingButton;
+    private TextView mNormalPriceText;
+    private TextView mPriceDropPrimaryText;
+    private TextView mPriceDropSecondaryText;
+
+    // Dependencies.
     private ImageFetcher mImageFetcher;
     private BookmarkModel mBookmarkModel;
 
@@ -48,11 +57,44 @@
     private SnackbarManager mSnackbarManager;
     private Profile mProfile;
 
+    private final int mPreferredImageSize;
+
     /**
-     * Constructor for inflating from XML.
+     * Factory constructor for building the view programmatically.
+     * @param context The calling context, usually the parent view.
+     * @param isVisualRefreshEnabled Whether to show the visual or compact bookmark row.
      */
+    public static PowerBookmarkShoppingItemRow buildView(
+            Context context, boolean isVisualRefreshEnabled) {
+        PowerBookmarkShoppingItemRow row = new PowerBookmarkShoppingItemRow(context, null);
+        BookmarkRow.buildView(row, context, isVisualRefreshEnabled);
+        return row;
+    }
+
+    /** Constructor for inflating from XML. */
     public PowerBookmarkShoppingItemRow(Context context, AttributeSet attrs) {
         super(context, attrs);
+
+        mPreferredImageSize = getResources().getDimensionPixelSize(
+                R.dimen.bookmark_refresh_preferred_start_icon_size);
+    }
+
+    // FrameLayout implementation.
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mPriceTrackingButton = findViewById(R.id.end_icon);
+        mPriceTrackingButton.setVisibility(View.GONE);
+
+        mCustomTextContainer = findViewById(R.id.custom_content_container);
+
+        LayoutInflater.from(getContext())
+                .inflate(R.layout.compact_price_drop_view, mCustomTextContainer);
+        mNormalPriceText = findViewById(R.id.normal_price_text);
+        mPriceDropPrimaryText = findViewById(R.id.price_drop_primary_text);
+        mPriceDropSecondaryText = findViewById(R.id.price_drop_secondary_text);
     }
 
     /**
@@ -68,7 +110,8 @@
         mProfile = profile;
     }
 
-    // BookmarkItemRow overrides:
+    // BookmarkItemRow implementation.
+
     @Override
     BookmarkItem setBookmarkId(
             BookmarkId bookmarkId, @Location int location, boolean fromFilterView) {
@@ -106,24 +149,24 @@
             long currentPrice) {
         assert mCurrencyFormatter != null;
 
-        mStartIconView.setOutlineProvider(
+        getIconView().setOutlineProvider(
                 new RoundedCornerOutlineProvider(getResources().getDimensionPixelSize(
                         R.dimen.list_item_v2_start_icon_corner_radius)));
-        mStartIconView.setClipToOutline(true);
+        getIconView().setClipToOutline(true);
         mImageFetcher.fetchImage(
                 ImageFetcher.Params.create(leadImageUrl, ImageFetcher.POWER_BOOKMARKS_CLIENT_NAME,
-                        mStartIconViewSize, mStartIconViewSize),
+                        mPreferredImageSize, mPreferredImageSize),
                 (image) -> {
                     if (image == null) return;
                     // We've successfully fetched an image. Cancel any pending requests for the
                     // favicon.
                     cancelFavicon();
-                    setStartIconDrawable(new BitmapDrawable(getResources(), image));
+                    setIconDrawable(new BitmapDrawable(getResources(), image));
                 });
 
         setPriceInfoChip(originalPrice, currentPrice);
         setPriceTrackingButton(priceTrackingEnabled);
-        mTitleView.setLabelFor(mEndStartButtonView.getId());
+        mTitleView.setLabelFor(mPriceTrackingButton.getId());
         PowerBookmarkMetrics.reportBookmarkShoppingItemRowPriceTrackingState(
                 PriceTrackingState.PRICE_TRACKING_SHOWN);
     }
@@ -133,20 +176,16 @@
         String formattedCurrentPrice = getFormattedCurrencyStringForPrice(currentPrice);
         // Note: chips should only be shown for price drops
         if (originalPrice <= currentPrice) {
-            TextView textView = new TextView(getContext(), null);
-            ApiCompatibilityUtils.setTextAppearance(
-                    textView, R.styleable.ChipView_primaryTextAppearance);
-            textView.setText(formattedCurrentPrice);
-            setCustomContent(textView);
+            mNormalPriceText.setText(formattedCurrentPrice);
+            mNormalPriceText.setVisibility(View.VISIBLE);
+            mPriceDropPrimaryText.setVisibility(View.GONE);
+            mPriceDropSecondaryText.setVisibility(View.GONE);
         } else {
-            ViewGroup row = (ViewGroup) LayoutInflater.from(getContext())
-                                    .inflate(R.layout.compact_price_drop_view, null, false);
-            TextView primaryText = row.findViewById(R.id.primary_text);
-            TextView secondaryText = row.findViewById(R.id.secondary_text);
-            setCustomContent(row);
-
-            assignPriceDropProperties(primaryText, secondaryText,
+            assignPriceDropProperties(mPriceDropPrimaryText, mPriceDropSecondaryText,
                     getFormattedCurrencyStringForPrice(originalPrice), formattedCurrentPrice);
+            mNormalPriceText.setVisibility(View.GONE);
+            mPriceDropPrimaryText.setVisibility(View.VISIBLE);
+            mPriceDropSecondaryText.setVisibility(View.VISIBLE);
         }
     }
 
@@ -164,10 +203,10 @@
     /** Sets up the button that allows you to un/subscribe to price-tracking updates. */
     private void setPriceTrackingButton(boolean priceTrackingEnabled) {
         mIsPriceTrackingEnabled = priceTrackingEnabled;
-        mEndStartButtonView.setContentDescription(getContext().getResources().getString(
+        mPriceTrackingButton.setContentDescription(getContext().getResources().getString(
                 priceTrackingEnabled ? R.string.disable_price_tracking_menu_item
                                      : R.string.enable_price_tracking_menu_item));
-        mEndStartButtonView.setVisibility(View.VISIBLE);
+        mPriceTrackingButton.setVisibility(View.VISIBLE);
         updatePriceTrackingImageForCurrentState();
         Callback<Boolean> subscriptionCallback = (success) -> {
             mSubscriptionChangeInProgress = false;
@@ -176,7 +215,7 @@
             mIsPriceTrackingEnabled = !mIsPriceTrackingEnabled;
             updatePriceTrackingImageForCurrentState();
         };
-        mEndStartButtonView.setOnClickListener((v) -> {
+        mPriceTrackingButton.setOnClickListener((v) -> {
             if (mSubscriptionChangeInProgress) return;
             mSubscriptionChangeInProgress = true;
 
@@ -190,7 +229,7 @@
     }
 
     private void updatePriceTrackingImageForCurrentState() {
-        mEndStartButtonView.setImageResource(mIsPriceTrackingEnabled
+        mPriceTrackingButton.setImageResource(mIsPriceTrackingEnabled
                         ? R.drawable.price_tracking_enabled_filled
                         : R.drawable.price_tracking_disabled);
     }
@@ -205,6 +244,6 @@
     }
 
     View getPriceTrackingButtonForTesting() {
-        return mEndStartButtonView;
+        return mPriceTrackingButton;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
index a781fc59..bdd368b5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorView.java
@@ -375,6 +375,18 @@
         createCompositorSurfaceManager();
     }
 
+    /**
+     * Enables/disables immersive VR overlay mode, a variant of overlay video mode.
+     * @param enabled Whether to enter or leave overlay immersive vr mode.
+     */
+    public void setOverlayVrMode(boolean enabled) {
+        mIsInXr = enabled;
+
+        // We're essentially entering OverlayVideo mode because we're going to be rendering to an
+        // overlay, but we don't actually need a new composite or to adjust the alpha blend.
+        mCompositorSurfaceManager.requestSurface(getSurfacePixelFormat());
+    }
+
     private int getSurfacePixelFormat() {
         if (mOverlayVideoEnabled || mAlwaysTranslucent) {
             return PixelFormat.TRANSLUCENT;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index f719731..7605581 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -67,6 +67,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
 import org.chromium.chrome.browser.toolbar.ControlContainer;
+import org.chromium.chrome.browser.toolbar.ToolbarFeatures;
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
 import org.chromium.chrome.features.start_surface.StartSurfaceUserData;
 import org.chromium.components.browser_ui.widget.TouchEventObserver;
@@ -1370,7 +1371,16 @@
         if (mUrlBar != null && mUrlBar.isFocused()) mUrlBar.clearFocus();
         boolean wasVisible = false;
         if (hasFocus()) {
-            wasVisible = KeyboardVisibilityDelegate.getInstance().hideKeyboard(this);
+            if (ToolbarFeatures.shouldDelayTransitionsForAnimation()) {
+                KeyboardVisibilityDelegate keyboardVisibilityDelegate =
+                        KeyboardVisibilityDelegate.getInstance();
+                wasVisible = keyboardVisibilityDelegate.isKeyboardShowing(getContext(), this);
+                if (wasVisible) {
+                    keyboardVisibilityDelegate.hideKeyboard(this);
+                }
+            } else {
+                wasVisible = KeyboardVisibilityDelegate.getInstance().hideKeyboard(this);
+            }
         }
         if (wasVisible) {
             mPostHideKeyboardTask = postHideTask;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
index 2809004c..42a95d63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
@@ -793,4 +793,9 @@
      */
     @LayoutType
     public abstract int getLayoutType();
+
+    /** Returns whether the layout is currently running animations. */
+    public boolean isRunningAnimations() {
+        return false;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
index f05ff8b..3a22616 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
@@ -59,6 +59,7 @@
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
 import org.chromium.chrome.browser.toolbar.ControlContainer;
+import org.chromium.chrome.browser.toolbar.ToolbarFeatures;
 import org.chromium.chrome.browser.toolbar.bottom.ScrollingBottomViewSceneLayer;
 import org.chromium.chrome.browser.toolbar.top.TopToolbarOverlayCoordinator;
 import org.chromium.chrome.browser.ui.native_page.NativePage;
@@ -473,14 +474,17 @@
         }
         mUpdateRequested = false;
 
-        // TODO(mdjones): Remove the time related params from this method. The new animation system
-        // has its own timer.
-        boolean areAnimatorsComplete = mAnimationHandler.pushUpdate();
-
         // TODO(crbug.com/1070281): Remove after the FrameRequestSupplier migrates to the animation
         //  system.
         final Layout layout = getActiveLayout();
 
+        // TODO(mdjones): Remove the time related params from this method. The new animation system
+        // has its own timer.
+        boolean areAnimatorsComplete = mAnimationHandler.pushUpdate();
+        if (layout != null && ToolbarFeatures.shouldDelayTransitionsForAnimation()) {
+            areAnimatorsComplete &= !layout.isRunningAnimations();
+        }
+
         // TODO(crbug.com/1070281): Layout itself should decide when it's done hiding and done
         //  showing.
         if (layout != null && layout.onUpdate(timeMs, dtMs) && areAnimatorsComplete) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherImpl.java
index e588431f..5c58cc8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherImpl.java
@@ -38,6 +38,7 @@
     private static final String TAG = "HelpAndFeedback";
 
     private static ProfileKeyedMap<HelpAndFeedbackLauncher> sProfileToLauncherMap;
+    private final HelpAndFeedbackLauncherDelegate mDelegate;
 
     private Profile mProfile;
 
@@ -61,6 +62,12 @@
         });
     }
 
+    // TODO(tedchoc): Reduce visibility to private and pass in the Profile once the de-AppHooks
+    //                changes land.
+    public HelpAndFeedbackLauncherImpl() {
+        mDelegate = new HelpAndFeedbackLauncherDelegateImpl();
+    }
+
     /**
      * Starts an activity showing a help page for the specified context ID.
      *
@@ -70,6 +77,7 @@
      *                    context and will be used to show a more relevant help page.
      * @param collector the {@link FeedbackCollector} to use for extra data. Must not be null.
      */
+    // TODO(tedchoc): Remove once the de-AppHooks changes land.
     protected void show(
             Activity activity, String helpContext, @Nonnull FeedbackCollector collector) {
         Log.d(TAG, "Feedback data: " + collector.getBundle());
@@ -83,6 +91,7 @@
      *                 screenshot of.
      * @param collector the {@link FeedbackCollector} to use for extra data. Must not be null.
      */
+    // TODO(tedchoc): Remove once the de-AppHooks changes land.
     protected void showFeedback(Activity activity, @Nonnull FeedbackCollector collector) {
         Log.d(TAG, "Feedback data: " + collector.getBundle());
         launchFallbackSupportUri(activity);
@@ -103,7 +112,7 @@
         new ChromeFeedbackCollector(activity, null /* categoryTag */, null /* description */,
                 new ScreenshotTask(activity),
                 new ChromeFeedbackCollector.InitParams(mProfile, url, helpContext),
-                collector -> show(activity, helpContext, collector), mProfile);
+                collector -> mDelegate.show(activity, helpContext, collector), mProfile);
     }
 
     /**
@@ -128,7 +137,7 @@
                         -> {
                     RecordHistogram.recordLongTimesHistogram("Feedback.Duration.FormOpenToSubmit",
                             SystemClock.elapsedRealtime() - startTime);
-                    showFeedback(activity, collector);
+                    mDelegate.showFeedback(activity, collector);
                 },
                 mProfile);
     }
@@ -161,7 +170,7 @@
         new FeedFeedbackCollector(activity, categoryTag, null /* description */,
                 new ScreenshotTask(activity),
                 new FeedFeedbackCollector.InitParams(mProfile, url, feedContext),
-                collector -> showFeedback(activity, collector), mProfile);
+                collector -> mDelegate.showFeedback(activity, collector), mProfile);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
index 5912e96..0b00207 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.history;
 
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
@@ -23,7 +22,6 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper.DefaultFaviconHelper;
 import org.chromium.chrome.browser.ui.favicon.FaviconUtils;
-import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.util.TraceEventVectorDrawableCompat;
 import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectableItemView;
@@ -56,9 +54,6 @@
         mIconGenerator = FaviconUtils.createCircularIconGenerator(context);
         mEndPadding =
                 context.getResources().getDimensionPixelSize(R.dimen.default_list_row_padding);
-
-        mStartIconSelectedColorList =
-                ColorStateList.valueOf(SemanticColorUtils.getDefaultIconColorInverse(context));
     }
 
     @Override
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 b7064c0..a023668 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
@@ -933,6 +933,9 @@
                 if (layoutType == LayoutType.TAB_SWITCHER) {
                     mToolbar.onTabSwitcherTransitionFinished();
                 }
+                if (ToolbarFeatures.shouldDelayTransitionsForAnimation()) {
+                    mToolbar.onTransitionEnd();
+                }
             }
 
             @Override
@@ -947,6 +950,9 @@
                         mControlContainer.invalidateBitmap();
                     }
                 }
+                if (ToolbarFeatures.shouldDelayTransitionsForAnimation()) {
+                    mToolbar.onTransitionStart();
+                }
             }
 
             @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRowTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRowTest.java
index 5afec90..b6cd095 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRowTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRowTest.java
@@ -97,10 +97,7 @@
                     ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
 
             getActivity().setContentView(mContentView, params);
-            mBookmarkItemRow = (BookmarkItemRow) getActivity()
-                                       .getLayoutInflater()
-                                       .inflate(R.layout.bookmark_item_row, mContentView, true)
-                                       .findViewById(R.id.bookmark_item_row);
+            mBookmarkItemRow = BookmarkManagerCoordinator.buildBookmarkItemView(mContentView);
             mBookmarkItemRow.setRoundedIconGeneratorForTesting(mRoundedIconGenerator);
             mBookmarkItemRow.onDelegateInitialized(mDelegate);
         });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowRenderTest.java
index fbdb916..d53d98e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowRenderTest.java
@@ -9,6 +9,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
@@ -95,7 +96,7 @@
 
     private Bitmap mBitmap;
     private PowerBookmarkShoppingItemRow mPowerBookmarkShoppingItemRow;
-    private ViewGroup mContentView;
+    private LinearLayout mContentView;
 
     public PowerBookmarkShoppingItemRowRenderTest(boolean nightModeEnabled) {
         // Sets a fake background color to make the screenshots easier to compare with bare eyes.
@@ -133,19 +134,18 @@
 
             FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
             mActivityTestRule.getActivity().setContentView(mContentView, params);
+
             mPowerBookmarkShoppingItemRow =
-                    (PowerBookmarkShoppingItemRow) mActivityTestRule.getActivity()
-                            .getLayoutInflater()
-                            .inflate(R.layout.power_bookmark_shopping_item_row, mContentView, true)
-                            .findViewById(R.id.power_bookmark_shopping_row);
+                    BookmarkManagerCoordinator.buildShoppingItemView(mContentView);
+            mContentView.addView(mPowerBookmarkShoppingItemRow);
             mPowerBookmarkShoppingItemRow.setBackgroundColor(
                     SemanticColorUtils.getDefaultBgColor(mActivityTestRule.getActivity()));
             ((TextView) mPowerBookmarkShoppingItemRow.findViewById(R.id.title))
                     .setText("Test Bookmark");
             ((TextView) mPowerBookmarkShoppingItemRow.findViewById(R.id.description))
                     .setText("http://google.com");
+            mPowerBookmarkShoppingItemRow.findViewById(R.id.more).setVisibility(View.VISIBLE);
             mPowerBookmarkShoppingItemRow.init(
                     mImageFetcher, mBookmarkModel, mSnackbarManager, mProfile);
             mPowerBookmarkShoppingItemRow.setCurrencyFormatterForTesting(mCurrencyFormatter);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowTest.java
index 38adeaa..55b66d3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowTest.java
@@ -95,10 +95,7 @@
 
             getActivity().setContentView(mContentView, params);
             mPowerBookmarkShoppingItemRow =
-                    (PowerBookmarkShoppingItemRow) getActivity()
-                            .getLayoutInflater()
-                            .inflate(R.layout.power_bookmark_shopping_item_row, mContentView, true)
-                            .findViewById(R.id.power_bookmark_shopping_row);
+                    BookmarkManagerCoordinator.buildShoppingItemView(mContentView);
             mPowerBookmarkShoppingItemRow.setBackgroundColor(
                     SemanticColorUtils.getDefaultBgColor(getActivity()));
             ((TextView) mPowerBookmarkShoppingItemRow.findViewById(R.id.title))
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java
index b13c6f5..a4cc33d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
+import org.chromium.chrome.browser.omnibox.action.OmniboxActionType;
 import org.chromium.chrome.browser.omnibox.action.OmniboxPedalType;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionView;
 import org.chromium.chrome.browser.tabmodel.IncognitoTabHostUtils;
@@ -111,8 +112,9 @@
     private AutocompleteMatch createDummyPedalSuggestion(String name, @OmniboxPedalType int id) {
         return AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                 .setDisplayText(name)
-                .setActions(Arrays.asList(new OmniboxPedal(id, "hints", "suggestionContents",
-                        "accessibilitySuffix", "accessibilityHint", GURL.emptyGURL())))
+                .setActions(Arrays.asList(
+                        new OmniboxPedal(OmniboxActionType.PEDAL, id, "hints", "suggestionContents",
+                                "accessibilitySuffix", "accessibilityHint", GURL.emptyGURL())))
                 .build();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsTest.java
index e026c63b..28d0011f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsTest.java
@@ -164,7 +164,7 @@
         for (int i = 0; i < coordinator.getSuggestionCount(); ++i) {
             AutocompleteMatch suggestion = coordinator.getSuggestionAt(i);
             if (suggestion != null && !suggestion.getActions().isEmpty()
-                    && suggestion.getActions().get(0).getPedalID() == pedalType) {
+                    && suggestion.getActions().get(0).getPedalId() == pedalType) {
                 return suggestion;
             }
         }
@@ -265,16 +265,16 @@
     private AutocompleteMatch createDummyPedalSuggestion(String name, @OmniboxPedalType int id) {
         return AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                 .setDisplayText(name)
-                .setActions(Arrays.asList(new OmniboxPedal(id, "hints", "suggestionContents",
-                        "accessibilitySuffix", "accessibilityHint", GURL.emptyGURL())))
+                .setActions(Arrays.asList(
+                        new OmniboxPedal(OmniboxActionType.PEDAL, id, "hints", "suggestionContents",
+                                "accessibilitySuffix", "accessibilityHint", GURL.emptyGURL())))
                 .build();
     }
 
     private AutocompleteMatch createDummyHistoryClustersAction(String name) {
         return AutocompleteMatchBuilder.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
                 .setDisplayText(name)
-                .setActions(Arrays.asList(new HistoryClustersAction(
-                        OmniboxActionType.HISTORY_CLUSTERS, "hints", "suggestionContents",
+                .setActions(Arrays.asList(new HistoryClustersAction("hints", "suggestionContents",
                         "accessibilitySuffix", "accessibilityHint", GURL.emptyGURL(), name)))
                 .build();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java
index ab86dcc..9600de1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java
@@ -59,7 +59,8 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @DoNotBatch(reason = "TODO(crbug.com/1168590): SyncTestRule doesn't support batching.")
-@EnableFeatures({ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE})
+@EnableFeatures({ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE,
+        ChromeFeatureList.MESSAGES_FOR_ANDROID_SYNC_ERROR})
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class SyncErrorMessageTest {
     @Mock
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/omnibox/ActionChipsDelegateImplUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/omnibox/ActionChipsDelegateImplUnitTest.java
index b9fa114..2101d197 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/app/omnibox/ActionChipsDelegateImplUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/omnibox/ActionChipsDelegateImplUnitTest.java
@@ -99,7 +99,7 @@
 
     /** List of all supported OmniboxActionTypes. */
     public static final Set<Integer> SUPPORTED_ACTIONS =
-            ImmutableSet.of(OmniboxActionType.HISTORY_CLUSTERS);
+            ImmutableSet.of(OmniboxActionType.PEDAL, OmniboxActionType.HISTORY_CLUSTERS);
 
     public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule();
     private @Mock HistoryClustersCoordinator mHistoryClustersCoordinator;
@@ -162,38 +162,46 @@
     }
 
     /**
+     * Create Omnibox Action action of a given type.
+     */
+    private OmniboxPedal buildAction(@OmniboxActionType int type) {
+        return new OmniboxPedal(type, OmniboxPedalType.NONE, "hint", "contents",
+                "accessibility suffix", "accessibility hint", null);
+    }
+
+    /**
      * Create Omnibox Pedal action of a given type.
      */
-    private OmniboxPedal buildAction(@OmniboxPedalType int type) {
-        return new OmniboxPedal(
-                type, "hint", "contents", "accessibility suffix", "accessibility hint", null);
+    private OmniboxPedal buildPedal(@OmniboxPedalType int type) {
+        return new OmniboxPedal(OmniboxActionType.PEDAL, type, "hint", "contents",
+                "accessibility suffix", "accessibility hint", null);
     }
 
     /**
      * Create HistoryCluster Action with a supplied query.
      */
     private HistoryClustersAction buildHistoryClustersAction(String query) {
-        return new HistoryClustersAction(OmniboxActionType.HISTORY_CLUSTERS, "hint", "contents",
-                "accessibility suffix", "accessibility hint", null, query);
+        return new HistoryClustersAction(
+                "hint", "contents", "accessibility suffix", "accessibility hint", null, query);
     }
 
     @Test
     public void executePedal_manageChromeSettings() {
-        mDelegate.execute(buildAction(OmniboxPedalType.MANAGE_CHROME_SETTINGS));
+        mDelegate.execute(buildPedal(OmniboxPedalType.MANAGE_CHROME_SETTINGS));
         checkSettingsActivityFragmentStarted(null);
         checkOmniboxPedalUsageRecorded(OmniboxPedalType.MANAGE_CHROME_SETTINGS);
     }
 
     @Test
     public void executePedal_clearBrowsingData() {
-        mDelegate.execute(buildAction(OmniboxPedalType.CLEAR_BROWSING_DATA));
+        mDelegate.execute(buildPedal(OmniboxPedalType.CLEAR_BROWSING_DATA));
         checkSettingsActivityFragmentStarted(ClearBrowsingDataTabsFragment.class);
         checkOmniboxPedalUsageRecorded(OmniboxPedalType.CLEAR_BROWSING_DATA);
     }
 
     @Test
     public void executePedal_managePasswords() {
-        mDelegate.execute(buildAction(OmniboxPedalType.MANAGE_PASSWORDS));
+        mDelegate.execute(buildPedal(OmniboxPedalType.MANAGE_PASSWORDS));
         assertTrue(ShadowPasswordManagerLauncher.sPasswordSettingsRequested);
         ShadowPasswordManagerLauncher.reset();
         checkOmniboxPedalUsageRecorded(OmniboxPedalType.MANAGE_PASSWORDS);
@@ -201,35 +209,35 @@
 
     @Test
     public void executePedal_updateCreditCard() {
-        mDelegate.execute(buildAction(OmniboxPedalType.UPDATE_CREDIT_CARD));
+        mDelegate.execute(buildPedal(OmniboxPedalType.UPDATE_CREDIT_CARD));
         checkSettingsActivityFragmentStarted(AutofillPaymentMethodsFragment.class);
         checkOmniboxPedalUsageRecorded(OmniboxPedalType.UPDATE_CREDIT_CARD);
     }
 
     @Test
     public void executePedal_runChromeSafetyCheck() {
-        mDelegate.execute(buildAction(OmniboxPedalType.RUN_CHROME_SAFETY_CHECK));
+        mDelegate.execute(buildPedal(OmniboxPedalType.RUN_CHROME_SAFETY_CHECK));
         checkSettingsActivityFragmentStarted(SafetyCheckSettingsFragment.class);
         checkOmniboxPedalUsageRecorded(OmniboxPedalType.RUN_CHROME_SAFETY_CHECK);
     }
 
     @Test
     public void executePedal_manageSiteSettings() {
-        mDelegate.execute(buildAction(OmniboxPedalType.MANAGE_SITE_SETTINGS));
+        mDelegate.execute(buildPedal(OmniboxPedalType.MANAGE_SITE_SETTINGS));
         checkSettingsActivityFragmentStarted(SiteSettings.class);
         checkOmniboxPedalUsageRecorded(OmniboxPedalType.MANAGE_SITE_SETTINGS);
     }
 
     @Test
     public void executePedal_manageChromeAccessibility() {
-        mDelegate.execute(buildAction(OmniboxPedalType.MANAGE_CHROME_ACCESSIBILITY));
+        mDelegate.execute(buildPedal(OmniboxPedalType.MANAGE_CHROME_ACCESSIBILITY));
         checkSettingsActivityFragmentStarted(AccessibilitySettings.class);
         checkOmniboxPedalUsageRecorded(OmniboxPedalType.MANAGE_CHROME_ACCESSIBILITY);
     }
 
     @Test
     public void executePedal_launchIncognito_fromCustomActivity() {
-        mDelegate.execute(buildAction(OmniboxPedalType.LAUNCH_INCOGNITO));
+        mDelegate.execute(buildPedal(OmniboxPedalType.LAUNCH_INCOGNITO));
 
         var intent = mShadowActivity.getNextStartedActivity();
         assertEquals(Intent.ACTION_VIEW, intent.getAction());
@@ -240,7 +248,7 @@
 
     @Test
     public void executePedal_viewChromeHistory_fromCustomActivity() {
-        mDelegate.execute(buildAction(OmniboxPedalType.VIEW_CHROME_HISTORY));
+        mDelegate.execute(buildPedal(OmniboxPedalType.VIEW_CHROME_HISTORY));
 
         var intent = mShadowActivity.getNextStartedActivity();
         assertEquals(HistoryActivity.class.getName(), intent.getComponent().getClassName());
@@ -251,7 +259,7 @@
 
     @Test
     public void executePedal_playChromeDinoGame_fromCustomActivity() {
-        mDelegate.execute(buildAction(OmniboxPedalType.PLAY_CHROME_DINO_GAME));
+        mDelegate.execute(buildPedal(OmniboxPedalType.PLAY_CHROME_DINO_GAME));
 
         var intent = mShadowActivity.getNextStartedActivity();
         assertEquals(Intent.ACTION_VIEW, intent.getAction());
@@ -288,7 +296,7 @@
                 ImmutableMap.of(OmniboxPedalType.PLAY_CHROME_DINO_GAME, R.drawable.ic_dino);
 
         for (var entry : SUPPORTED_PEDALS) {
-            var icon = mDelegate.getIcon(buildAction(entry));
+            var icon = mDelegate.getIcon(buildPedal(entry));
 
             var expectedIconRes =
                     customResourceMap.getOrDefault(entry, R.drawable.fre_product_logo);
@@ -312,13 +320,13 @@
 
             // "Local variables referenced by lambda must be effectively final"
             final int pedalType = type;
-            assertThrows(AssertionError.class, () -> mDelegate.getIcon(buildAction(pedalType)));
+            assertThrows(AssertionError.class, () -> mDelegate.getIcon(buildPedal(pedalType)));
         }
     }
 
     @Test
     public void verifyDecorations_omniboxActions() {
-        var icon = mDelegate.getIcon(buildAction(OmniboxActionType.HISTORY_CLUSTERS));
+        var icon = mDelegate.getIcon(buildHistoryClustersAction("test"));
         assertEquals(R.drawable.ic_journeys, icon.iconRes);
         assertTrue(icon.tintWithTextColor);
     }
@@ -328,7 +336,7 @@
         // This test catches introduction of any new action types to make sure we
         // account for these actions in the verifyDecorations_omniboxActions test.
         // Guarantees that no new actions can be enabled without proper test coverage.
-        for (int type = OmniboxActionType.FIRST; type < OmniboxActionType.LAST; type++) {
+        for (int type = OmniboxActionType.UNKNOWN; type <= OmniboxActionType.LAST; type++) {
             // Skip past actions we already know we support.
             if (SUPPORTED_ACTIONS.contains(type)) continue;
 
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index 768eddf..9596e34 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-113.0.5635.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-114.0.5676.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chrome_exe_main_win.cc b/chrome/app/chrome_exe_main_win.cc
index 2323e25..b98f498 100644
--- a/chrome/app/chrome_exe_main_win.cc
+++ b/chrome/app/chrome_exe_main_win.cc
@@ -315,7 +315,7 @@
   // emplaced.
   // Note: The DLL is patched separately, in chrome/app/chrome_main.cc.
   base::debug::HandleHooks::AddIATPatch(CURRENT_MODULE());
-#endif  // !defined(COMPONENT_BUILD) && !DCHECK_IS_ON()
+#endif  // !defined(COMPONENT_BUILD) && DCHECK_IS_ON()
 
   // Confirm that an explicit prefetch profile is used for all process types
   // except for the browser process. Any new process type will have to assign
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index f3d9170..850e42d6 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -11,12 +11,6 @@
   <message name="IDS_SETTINGS_ABOUT_PROGRAM" desc="Menu title for the About Chromium page.">
     About Chromium
   </message>
-  <message name="IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM" desc="Section title for the 'Get the most out of Chromium' page.">
-    Get the most ouf of Chromium
-  </message>
-  <message name="IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION" desc="Explanatory text for the link to the 'Get the most out of Chromium' page.">
-    This guide helps you understand your choices, so that Chromium works the way you want to
-  </message>
   <message name="IDS_SETTINGS_GET_HELP_USING_CHROME" desc="Text of the button which takes the user to the Chrome help page.">
     Get help with Chromium
   </message>
diff --git a/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1 b/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1
deleted file mode 100644
index 9cdb789..0000000
--- a/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2eb8c42afba366b6db5ca4bf4043d5665f18cd74
\ No newline at end of file
diff --git a/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1 b/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1
deleted file mode 100644
index 9cdb789..0000000
--- a/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2eb8c42afba366b6db5ca4bf4043d5665f18cd74
\ No newline at end of file
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index 242c3c7..2fd0bbe 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -11,12 +11,6 @@
   <message name="IDS_SETTINGS_ABOUT_PROGRAM" desc="Menu title for the About Chrome page.">
     About Chrome
   </message>
-  <message name="IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM" desc="Section title for the 'Get the most out of Chrome' page.">
-    Get the most ouf of Chrome
-  </message>
-  <message name="IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION" desc="Explanatory text for the link to the 'Get the most out of Chrome' page.">
-    This guide helps you understand your choices, so that Chrome works the way you want to
-  </message>
   <message name="IDS_SETTINGS_GET_HELP_USING_CHROME" desc="Text of the button which takes the user to the Chrome help page.">
     Get help with Chrome
   </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index a2e0a9e5..159a2cd 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -50,6 +50,12 @@
     <message name="IDS_SETTINGS_ABOUT_PAGE_SEND_FEEDBACK" desc="Text of the button which allows the user to send feedback with Chrome.">
       Send feedback
     </message>
+    <message name="IDS_SETTINGS_GET_THE_MOST_OUT_OF_CHROME" desc="Section title for the 'Get the most out of Chrome' page.">
+      Get the most ouf of Chrome
+    </message>
+    <message name="IDS_SETTINGS_GET_THE_MOST_OUT_OF_CHROME_DESCRIPTION" desc="Explanatory text for the link to the 'Get the most out of Chrome' page.">
+      This guide helps you understand your choices, so that Chrome works the way you want to
+    </message>
   </if>
   <if expr="is_macosx">
     <message name="IDS_ABOUT_CHROME_AUTOUPDATE_ALL" desc="The 'Automatically update Chrome for all users.' button in the About window.  Mac-only.">
diff --git a/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_CHROME.png.sha1
similarity index 100%
rename from chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM.png.sha1
rename to chrome/app/settings_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_CHROME.png.sha1
diff --git a/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_CHROME_DESCRIPTION.png.sha1
similarity index 100%
rename from chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION.png.sha1
rename to chrome/app/settings_strings_grdp/IDS_SETTINGS_GET_THE_MOST_OUT_OF_CHROME_DESCRIPTION.png.sha1
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6eee5f9..23e1682 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1404,6 +1404,13 @@
      kJourneysSearchVisitsWithPairwiseMergeParams,
      std::size(kJourneysSearchVisitsWithPairwiseMergeParams), nullptr},
 };
+const FeatureEntry::FeatureParam kJourneysImagesCoverParams[] = {
+    {"JourneysImagesCover", "false"},
+};
+const FeatureEntry::FeatureVariation kJourneysImagesVariations[] = {
+    {"Image Does Not Cover Container", kJourneysImagesCoverParams,
+     std::size(kJourneysImagesCoverParams), nullptr},
+};
 const FeatureEntry::FeatureParam
     kJourneysLabelsWithSearchVisitEntitiesParams[] = {
         {"labels_from_search_visit_entities", "true"},
@@ -5781,7 +5788,9 @@
 
     {"history-journeys-images", flag_descriptions::kJourneysImagesName,
      flag_descriptions::kJourneysImagesDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(history_clusters::internal::kJourneysImages)},
+     FEATURE_WITH_PARAMS_VALUE_TYPE(history_clusters::internal::kJourneysImages,
+                                    kJourneysImagesVariations,
+                                    "HistoryJourneysImages")},
 
     {"history-journeys-labels", flag_descriptions::kJourneysLabelsName,
      flag_descriptions::kJourneysLabelsDescription, kOsDesktop | kOsAndroid,
@@ -9842,11 +9851,11 @@
      FEATURE_VALUE_TYPE(ash::features::kDeskButton)},
 #endif
 
-#if !BUILDFLAG(IS_ANDROID)
-    {"settings-enable-get-the-most-out-of-program",
-     flag_descriptions::kSettingsEnableGetTheMostOutOfProgramName,
-     flag_descriptions::kSettingsEnableGetTheMostOutOfProgramDescription,
-     kOsDesktop, FEATURE_VALUE_TYPE(::features::kGetTheMostOutOfProgram)},
+#if !BUILDFLAG(IS_ANDROID) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+    {"settings-enable-get-the-most-out-of-chrome",
+     flag_descriptions::kSettingsEnableGetTheMostOutOfChromeName,
+     flag_descriptions::kSettingsEnableGetTheMostOutOfChromeDescription,
+     kOsDesktop, FEATURE_VALUE_TYPE(::features::kGetTheMostOutOfChrome)},
 #endif
 
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
@@ -9858,6 +9867,13 @@
          autofill::features::kAutofillEnablePaymentsMandatoryReauth)},
 #endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
 
+    {"autofill-enable-email-otp-for-vcn-yellow-path",
+     flag_descriptions::kAutofillEnableEmailOtpForVcnYellowPathName,
+     flag_descriptions::kAutofillEnableEmailOtpForVcnYellowPathDescription,
+     kOsAll,
+     FEATURE_VALUE_TYPE(
+         autofill::features::kAutofillEnableEmailOtpForVcnYellowPath)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
index 144fb427..7373ba0 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
+++ b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
@@ -5,17 +5,25 @@
 #include "chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h"
 
 #include <memory>
+#include <string>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/functional/callback_forward.h"
 #include "build/build_config.h"
 #include "chrome/browser/accessibility/caption_bubble_context_browser.h"
 #include "chrome/browser/accessibility/live_caption/live_caption_controller_factory.h"
+#include "chrome/browser/accessibility/live_translate_controller_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/live_caption/live_caption_controller.h"
+#include "components/live_caption/live_translate_controller.h"
+#include "components/live_caption/pref_names.h"
 #include "components/live_caption/views/caption_bubble_model.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "media/base/media_switches.h"
+#include "media/mojo/mojom/speech_recognition_result.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 namespace captions {
@@ -42,6 +50,8 @@
   if (!web_contents)
     return;
   Observe(web_contents);
+  prefs_ = Profile::FromBrowserContext(GetWebContents()->GetBrowserContext())
+               ->GetPrefs();
   context_ = CaptionBubbleContextBrowser::Create(web_contents);
 }
 
@@ -59,8 +69,22 @@
     std::move(reply).Run(false);
     return;
   }
-  std::move(reply).Run(
-      live_caption_controller->DispatchTranscription(context_.get(), result));
+
+  if (base::FeatureList::IsEnabled(media::kLiveTranslate) &&
+      prefs_->GetBoolean(prefs::kLiveTranslateEnabled)) {
+    std::string source_language =
+        prefs_->GetString(prefs::kLiveCaptionLanguageCode);
+    std::string target_language =
+        prefs_->GetString(prefs::kLiveTranslateTargetLanguageCode);
+    GetLiveTranslateController()->GetTranslation(
+        result, source_language, target_language,
+        base::BindOnce(&LiveCaptionSpeechRecognitionHost::OnTranslationCallback,
+                       weak_factory_.GetWeakPtr()));
+    std::move(reply).Run(!stop_transcriptions_);
+  } else {
+    std::move(reply).Run(
+        live_caption_controller->DispatchTranscription(context_.get(), result));
+  }
 }
 
 void LiveCaptionSpeechRecognitionHost::OnLanguageIdentificationEvent(
@@ -97,6 +121,13 @@
 }
 #endif
 
+void LiveCaptionSpeechRecognitionHost::OnTranslationCallback(
+    media::SpeechRecognitionResult result) {
+  LiveCaptionController* live_caption_controller = GetLiveCaptionController();
+  stop_transcriptions_ =
+      !live_caption_controller->DispatchTranscription(context_.get(), result);
+}
+
 content::WebContents* LiveCaptionSpeechRecognitionHost::GetWebContents() {
   return content::WebContents::FromRenderFrameHost(&render_frame_host());
 }
@@ -113,4 +144,18 @@
   return LiveCaptionControllerFactory::GetForProfile(profile);
 }
 
+LiveTranslateController*
+LiveCaptionSpeechRecognitionHost::GetLiveTranslateController() {
+  content::WebContents* web_contents = GetWebContents();
+  if (!web_contents) {
+    return nullptr;
+  }
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  if (!profile) {
+    return nullptr;
+  }
+  return LiveTranslateControllerFactory::GetForProfile(profile);
+}
+
 }  // namespace captions
diff --git a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
index e262f877..78458e1 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
+++ b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
@@ -7,12 +7,15 @@
 
 #include <memory>
 
+#include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
 #include "content/public/browser/document_service.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "media/mojo/mojom/speech_recognition.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 
+class PrefService;
+
 namespace content {
 class RenderFrameHost;
 }
@@ -21,6 +24,7 @@
 
 class CaptionBubbleContextBrowser;
 class LiveCaptionController;
+class LiveTranslateController;
 
 ///////////////////////////////////////////////////////////////////////////////
 //  Live Caption Speech Recognition Host
@@ -68,16 +72,30 @@
       mojo::PendingReceiver<media::mojom::SpeechRecognitionRecognizerClient>
           pending_receiver);
   ~LiveCaptionSpeechRecognitionHost() override;
+  void OnTranslationCallback(media::SpeechRecognitionResult result);
 
   // Returns the WebContents if it exists. If it does not exist, sets the
   // RenderFrameHost reference to nullptr and returns nullptr.
   content::WebContents* GetWebContents();
 
   // Returns the LiveCaptionController for frame_host_. Returns nullptr if it
-  // does not exist.
+  // does not exist. Lifetime is tied to the BrowserContext.
   LiveCaptionController* GetLiveCaptionController();
 
+  // Returns the LiveTranslateController for frame_host_. Returns nullptr if it
+  // does not exist. Lifetime is tied to the BrowserContext.
+  LiveTranslateController* GetLiveTranslateController();
+
   std::unique_ptr<CaptionBubbleContextBrowser> context_;
+
+  // A flag used by the Live Translate feature indicating whether transcriptions
+  // should stop.
+  bool stop_transcriptions_ = false;
+
+  // The user preferences containing the target and source language codes.
+  PrefService* prefs_;
+
+  base::WeakPtrFactory<LiveCaptionSpeechRecognitionHost> weak_factory_{this};
 };
 
 }  // namespace captions
diff --git a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc
index 8b7ebd0..c1d8e3b 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc
+++ b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc
@@ -243,4 +243,24 @@
 }
 #endif
 
+IN_PROC_BROWSER_TEST_F(LiveCaptionSpeechRecognitionHostTest, LiveTranslate) {
+  content::RenderFrameHost* frame_host = browser()
+                                             ->tab_strip_model()
+                                             ->GetActiveWebContents()
+                                             ->GetPrimaryMainFrame();
+  CreateLiveCaptionSpeechRecognitionHost(frame_host);
+
+  SetLiveCaptionEnabled(true);
+  SetLiveTranslateEnabled(true);
+
+  // Dispatching the speech recognition should be successful, but the
+  // widget should remain hidden because the test does not fetch real
+  // translations.
+  OnSpeechRecognitionRecognitionEvent(
+      frame_host, "Elephants can live over 90 years in captivity.",
+      /* expected_success= */ true);
+  base::RunLoop().RunUntilIdle();
+  ExpectIsWidgetVisible(false);
+}
+
 }  // namespace captions
diff --git a/chrome/browser/accessibility/live_caption/live_caption_test_util.cc b/chrome/browser/accessibility/live_caption/live_caption_test_util.cc
index ddc2b5e3..49cde9c 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_test_util.cc
+++ b/chrome/browser/accessibility/live_caption/live_caption_test_util.cc
@@ -33,7 +33,8 @@
 
 // Chrome feature flags that gate Live Caption.
 std::vector<base::test::FeatureRef> RequiredFeatureFlags() {
-  std::vector<base::test::FeatureRef> features = {media::kLiveCaption};
+  std::vector<base::test::FeatureRef> features = {media::kLiveCaption,
+                                                  media::kLiveTranslate};
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   features.push_back(ash::features::kOnDeviceSpeechRecognition);
 #endif
@@ -79,4 +80,9 @@
   }
 }
 
+void LiveCaptionBrowserTest::SetLiveTranslateEnabled(bool enabled) {
+  browser()->profile()->GetPrefs()->SetBoolean(prefs::kLiveTranslateEnabled,
+                                               enabled);
+}
+
 }  // namespace captions
diff --git a/chrome/browser/accessibility/live_caption/live_caption_test_util.h b/chrome/browser/accessibility/live_caption/live_caption_test_util.h
index ca529bd..6fc0121d 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_test_util.h
+++ b/chrome/browser/accessibility/live_caption/live_caption_test_util.h
@@ -29,8 +29,12 @@
   // Enables/disables the live caption pref on the specified profile (or default
   // profile) and marks the SODA library as installed.
   void SetLiveCaptionEnabled(bool enabled);
+
   void SetLiveCaptionEnabledOnProfile(bool enabled, Profile* profile);
 
+  // Enables/disables the live translate pref.
+  void SetLiveTranslateEnabled(bool enabled);
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index f6eced84..aa1ae38 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -3102,8 +3102,8 @@
     "wallpaper/wallpaper_drivefs_delegate_impl.h",
     "wallpaper/wallpaper_enumerator.cc",
     "wallpaper/wallpaper_enumerator.h",
-    "wallpaper_handlers/backdrop_fetcher_delegate.cc",
-    "wallpaper_handlers/backdrop_fetcher_delegate.h",
+    "wallpaper_handlers/wallpaper_fetcher_delegate.cc",
+    "wallpaper_handlers/wallpaper_fetcher_delegate.h",
     "wallpaper_handlers/wallpaper_handlers.cc",
     "wallpaper_handlers/wallpaper_handlers.h",
     "wallpaper_handlers/wallpaper_handlers_metric_utils.cc",
@@ -4521,8 +4521,8 @@
     "settings/scoped_testing_cros_settings.h",
     "wallpaper_handlers/mock_wallpaper_handlers.cc",
     "wallpaper_handlers/mock_wallpaper_handlers.h",
-    "wallpaper_handlers/test_backdrop_fetcher_delegate.cc",
-    "wallpaper_handlers/test_backdrop_fetcher_delegate.h",
+    "wallpaper_handlers/test_wallpaper_fetcher_delegate.cc",
+    "wallpaper_handlers/test_wallpaper_fetcher_delegate.h",
     "web_applications/personalization_app/personalization_app_browsertest_fixture.cc",
     "web_applications/personalization_app/personalization_app_browsertest_fixture.h",
   ]
diff --git a/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc b/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc
index b11e4c2..32f9aad05 100644
--- a/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc
+++ b/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/functional/bind.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
 #include "chrome/browser/image_decoder/image_decoder.h"
 #include "chrome/browser/ui/ash/test_wallpaper_controller.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client_impl.h"
@@ -88,7 +88,7 @@
     // Wallpaper
     wallpaper_controller_client_ = std::make_unique<
         WallpaperControllerClientImpl>(
-        std::make_unique<wallpaper_handlers::TestBackdropFetcherDelegate>());
+        std::make_unique<wallpaper_handlers::TestWallpaperFetcherDelegate>());
     wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
 
     // Arc services
diff --git a/chrome/browser/ash/crosapi/wallpaper_ash_unittest.cc b/chrome/browser/ash/crosapi/wallpaper_ash_unittest.cc
index 1b35b05..e223b85 100644
--- a/chrome/browser/ash/crosapi/wallpaper_ash_unittest.cc
+++ b/chrome/browser/ash/crosapi/wallpaper_ash_unittest.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/ash/crosapi/wallpaper_ash.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
-#include "chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
 #include "chrome/browser/ui/ash/test_wallpaper_controller.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client_impl.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -72,7 +72,7 @@
     // Create Wallpaper Controller Client.
     wallpaper_controller_client_ = std::make_unique<
         WallpaperControllerClientImpl>(
-        std::make_unique<wallpaper_handlers::TestBackdropFetcherDelegate>());
+        std::make_unique<wallpaper_handlers::TestWallpaperFetcherDelegate>());
     wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
   }
 
diff --git a/chrome/browser/ash/login/demo_mode/demo_session_unittest.cc b/chrome/browser/ash/login/demo_mode/demo_session_unittest.cc
index 25602e7..37735e9a 100644
--- a/chrome/browser/ash/login/demo_mode/demo_session_unittest.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_session_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/timer/mock_timer.h"
 #include "chrome/browser/ash/login/demo_mode/demo_components.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/component_updater/fake_cros_component_manager.h"
@@ -75,7 +75,7 @@
     session_manager_ = std::make_unique<session_manager::SessionManager>();
     wallpaper_controller_client_ = std::make_unique<
         WallpaperControllerClientImpl>(
-        std::make_unique<wallpaper_handlers::TestBackdropFetcherDelegate>());
+        std::make_unique<wallpaper_handlers::TestWallpaperFetcherDelegate>());
     wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
   }
 
diff --git a/chrome/browser/ash/login/users/user_manager_unittest.cc b/chrome/browser/ash/login/users/user_manager_unittest.cc
index 12b5d708..4b43ab8 100644
--- a/chrome/browser/ash/login/users/user_manager_unittest.cc
+++ b/chrome/browser/ash/login/users/user_manager_unittest.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/ash/policy/core/device_local_account.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
-#include "chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -164,7 +164,7 @@
 
     wallpaper_controller_client_ = std::make_unique<
         WallpaperControllerClientImpl>(
-        std::make_unique<wallpaper_handlers::TestBackdropFetcherDelegate>());
+        std::make_unique<wallpaper_handlers::TestWallpaperFetcherDelegate>());
     wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
   }
 
diff --git a/chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.h b/chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.h
deleted file mode 100644
index e83712d..0000000
--- a/chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_BACKDROP_FETCHER_DELEGATE_H_
-#define CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_BACKDROP_FETCHER_DELEGATE_H_
-
-#include <memory>
-#include <string>
-
-namespace wallpaper_handlers {
-
-class BackdropCollectionInfoFetcher;
-class BackdropImageInfoFetcher;
-
-// Delegate class for creating backdrop fetchers. Abstract class to allow
-// mocking out in test.
-class BackdropFetcherDelegate {
- public:
-  virtual ~BackdropFetcherDelegate() = default;
-
-  virtual std::unique_ptr<BackdropCollectionInfoFetcher>
-  CreateBackdropCollectionInfoFetcher() const = 0;
-
-  virtual std::unique_ptr<BackdropImageInfoFetcher>
-  CreateBackdropImageInfoFetcher(const std::string& collection_id) const = 0;
-};
-
-class BackdropFetcherDelegateImpl : public BackdropFetcherDelegate {
- public:
-  BackdropFetcherDelegateImpl();
-
-  BackdropFetcherDelegateImpl(const BackdropFetcherDelegateImpl&) = delete;
-  BackdropFetcherDelegateImpl& operator=(const BackdropFetcherDelegateImpl&) =
-      delete;
-
-  ~BackdropFetcherDelegateImpl() override;
-
-  // BackdropFetcherDelegate:
-  std::unique_ptr<BackdropCollectionInfoFetcher>
-  CreateBackdropCollectionInfoFetcher() const override;
-
-  std::unique_ptr<BackdropImageInfoFetcher> CreateBackdropImageInfoFetcher(
-      const std::string& collection_id) const override;
-};
-
-}  // namespace wallpaper_handlers
-
-#endif  // CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_BACKDROP_FETCHER_DELEGATE_H_
diff --git a/chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h b/chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h
deleted file mode 100644
index 235ed40..0000000
--- a/chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_TEST_BACKDROP_FETCHER_DELEGATE_H_
-#define CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_TEST_BACKDROP_FETCHER_DELEGATE_H_
-
-#include <memory>
-#include <string>
-
-#include "chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.h"
-
-namespace wallpaper_handlers {
-
-class BackdropCollectionInfoFetcher;
-class BackdropImageInfoFetcher;
-
-class TestBackdropFetcherDelegate : public BackdropFetcherDelegate {
- public:
-  TestBackdropFetcherDelegate();
-
-  TestBackdropFetcherDelegate(const TestBackdropFetcherDelegate&) = delete;
-  TestBackdropFetcherDelegate& operator=(const TestBackdropFetcherDelegate&) =
-      delete;
-
-  ~TestBackdropFetcherDelegate() override;
-
-  // BackdropFetcherDelegate:
-  std::unique_ptr<BackdropCollectionInfoFetcher>
-  CreateBackdropCollectionInfoFetcher() const override;
-  std::unique_ptr<BackdropImageInfoFetcher> CreateBackdropImageInfoFetcher(
-      const std::string& collection_id) const override;
-};
-
-}  // namespace wallpaper_handlers
-#endif  // CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_TEST_BACKDROP_FETCHER_DELEGATE_H_
diff --git a/chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.cc b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc
similarity index 67%
rename from chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.cc
rename to chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc
index 62fb88a..9e5c220 100644
--- a/chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.cc
+++ b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
 
 #include <memory>
 #include <string>
@@ -13,18 +13,18 @@
 
 namespace wallpaper_handlers {
 
-TestBackdropFetcherDelegate::TestBackdropFetcherDelegate() = default;
+TestWallpaperFetcherDelegate::TestWallpaperFetcherDelegate() = default;
 
-TestBackdropFetcherDelegate::~TestBackdropFetcherDelegate() = default;
+TestWallpaperFetcherDelegate::~TestWallpaperFetcherDelegate() = default;
 
 std::unique_ptr<BackdropCollectionInfoFetcher>
-TestBackdropFetcherDelegate::CreateBackdropCollectionInfoFetcher() const {
+TestWallpaperFetcherDelegate::CreateBackdropCollectionInfoFetcher() const {
   return std::make_unique<
       testing::NiceMock<MockBackdropCollectionInfoFetcher>>();
 }
 
 std::unique_ptr<BackdropImageInfoFetcher>
-TestBackdropFetcherDelegate::CreateBackdropImageInfoFetcher(
+TestWallpaperFetcherDelegate::CreateBackdropImageInfoFetcher(
     const std::string& collection_id) const {
   return std::make_unique<testing::NiceMock<MockBackdropImageInfoFetcher>>(
       collection_id);
diff --git a/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h
new file mode 100644
index 0000000..b544537
--- /dev/null
+++ b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h
@@ -0,0 +1,36 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_TEST_WALLPAPER_FETCHER_DELEGATE_H_
+#define CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_TEST_WALLPAPER_FETCHER_DELEGATE_H_
+
+#include <memory>
+#include <string>
+
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h"
+
+namespace wallpaper_handlers {
+
+class BackdropCollectionInfoFetcher;
+class BackdropImageInfoFetcher;
+
+class TestWallpaperFetcherDelegate : public WallpaperFetcherDelegate {
+ public:
+  TestWallpaperFetcherDelegate();
+
+  TestWallpaperFetcherDelegate(const TestWallpaperFetcherDelegate&) = delete;
+  TestWallpaperFetcherDelegate& operator=(const TestWallpaperFetcherDelegate&) =
+      delete;
+
+  ~TestWallpaperFetcherDelegate() override;
+
+  // WallpaperFetcherDelegate:
+  std::unique_ptr<BackdropCollectionInfoFetcher>
+  CreateBackdropCollectionInfoFetcher() const override;
+  std::unique_ptr<BackdropImageInfoFetcher> CreateBackdropImageInfoFetcher(
+      const std::string& collection_id) const override;
+};
+
+}  // namespace wallpaper_handlers
+#endif  // CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_TEST_WALLPAPER_FETCHER_DELEGATE_H_
diff --git a/chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.cc b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc
similarity index 68%
rename from chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.cc
rename to chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc
index 21e5762c..3f20884 100644
--- a/chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.cc
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h"
 
 #include <memory>
 #include <string>
@@ -12,18 +12,18 @@
 
 namespace wallpaper_handlers {
 
-BackdropFetcherDelegateImpl::BackdropFetcherDelegateImpl() = default;
+WallpaperFetcherDelegateImpl::WallpaperFetcherDelegateImpl() = default;
 
-BackdropFetcherDelegateImpl::~BackdropFetcherDelegateImpl() = default;
+WallpaperFetcherDelegateImpl::~WallpaperFetcherDelegateImpl() = default;
 
 std::unique_ptr<BackdropCollectionInfoFetcher>
-BackdropFetcherDelegateImpl::CreateBackdropCollectionInfoFetcher() const {
+WallpaperFetcherDelegateImpl::CreateBackdropCollectionInfoFetcher() const {
   // Use `WrapUnique` to access the protected constructor.
   return absl::WrapUnique(new BackdropCollectionInfoFetcher());
 }
 
 std::unique_ptr<BackdropImageInfoFetcher>
-BackdropFetcherDelegateImpl::CreateBackdropImageInfoFetcher(
+WallpaperFetcherDelegateImpl::CreateBackdropImageInfoFetcher(
     const std::string& collection_id) const {
   // Use `WrapUnique` to access the protected constructor.
   return absl::WrapUnique(new BackdropImageInfoFetcher(collection_id));
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h
new file mode 100644
index 0000000..0be12850
--- /dev/null
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h
@@ -0,0 +1,49 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_WALLPAPER_FETCHER_DELEGATE_H_
+#define CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_WALLPAPER_FETCHER_DELEGATE_H_
+
+#include <memory>
+#include <string>
+
+namespace wallpaper_handlers {
+
+class BackdropCollectionInfoFetcher;
+class BackdropImageInfoFetcher;
+
+// Delegate class for creating backdrop fetchers. Abstract class to allow
+// mocking out in test.
+class WallpaperFetcherDelegate {
+ public:
+  virtual ~WallpaperFetcherDelegate() = default;
+
+  virtual std::unique_ptr<BackdropCollectionInfoFetcher>
+  CreateBackdropCollectionInfoFetcher() const = 0;
+
+  virtual std::unique_ptr<BackdropImageInfoFetcher>
+  CreateBackdropImageInfoFetcher(const std::string& collection_id) const = 0;
+};
+
+class WallpaperFetcherDelegateImpl : public WallpaperFetcherDelegate {
+ public:
+  WallpaperFetcherDelegateImpl();
+
+  WallpaperFetcherDelegateImpl(const WallpaperFetcherDelegateImpl&) = delete;
+  WallpaperFetcherDelegateImpl& operator=(const WallpaperFetcherDelegateImpl&) =
+      delete;
+
+  ~WallpaperFetcherDelegateImpl() override;
+
+  // WallpaperFetcherDelegate:
+  std::unique_ptr<BackdropCollectionInfoFetcher>
+  CreateBackdropCollectionInfoFetcher() const override;
+
+  std::unique_ptr<BackdropImageInfoFetcher> CreateBackdropImageInfoFetcher(
+      const std::string& collection_id) const override;
+};
+
+}  // namespace wallpaper_handlers
+
+#endif  // CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_WALLPAPER_FETCHER_DELEGATE_H_
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h
index 82a897b..0a6da7a 100644
--- a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h
@@ -57,13 +57,13 @@
   virtual void Start(OnCollectionsInfoFetched callback);
 
  protected:
-  // Protected constructor forces creation via `BackdropFetcherDelegate` to
+  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
   // allow mocking in test code.
   BackdropCollectionInfoFetcher();
 
  private:
   // Allow delegate to view the constructor.
-  friend class BackdropFetcherDelegateImpl;
+  friend class WallpaperFetcherDelegateImpl;
 
   // Called when the collections info download completes.
   void OnResponseFetched(const std::string& response);
@@ -93,13 +93,13 @@
   virtual void Start(OnImagesInfoFetched callback);
 
  protected:
-  // Protected constructor forces creation via `BackdropFetcherDelegate` to
+  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
   // allow mocking in test code.
   explicit BackdropImageInfoFetcher(const std::string& collection_id);
 
  private:
   // Allow delegate to view the constructor.
-  friend class BackdropFetcherDelegateImpl;
+  friend class WallpaperFetcherDelegateImpl;
 
   // Called when the images info download completes.
   void OnResponseFetched(const std::string& response);
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_browsertest_fixture.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_browsertest_fixture.cc
index d32ae73..b18e76d 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_browsertest_fixture.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_browsertest_fixture.cc
@@ -16,7 +16,7 @@
 #include "ash/webui/personalization_app/test/fake_personalization_app_keyboard_backlight_provider.h"
 #include "ash/webui/personalization_app/test/fake_personalization_app_theme_provider.h"
 #include "ash/webui/personalization_app/test/fake_personalization_app_user_provider.h"
-#include "chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_utils.h"
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h"
 #include "chrome/browser/profiles/profile.h"
@@ -37,7 +37,7 @@
   auto wallpaper_provider =
       std::make_unique<PersonalizationAppWallpaperProviderImpl>(
           web_ui,
-          std::make_unique<wallpaper_handlers::TestBackdropFetcherDelegate>());
+          std::make_unique<wallpaper_handlers::TestWallpaperFetcherDelegate>());
   auto user_provider =
       std::make_unique<FakePersonalizationAppUserProvider>(web_ui);
   return std::make_unique<PersonalizationAppUI>(
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_utils.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_utils.cc
index 1b97a880..c83ab4f 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_utils.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_utils.cc
@@ -6,7 +6,7 @@
 
 #include "base/notreached.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
-#include "chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h"
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_ambient_provider_impl.h"
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.h"
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_theme_provider_impl.h"
@@ -33,7 +33,7 @@
   auto wallpaper_provider = std::make_unique<
       ash::personalization_app::PersonalizationAppWallpaperProviderImpl>(
       web_ui,
-      std::make_unique<wallpaper_handlers::BackdropFetcherDelegateImpl>());
+      std::make_unique<wallpaper_handlers::WallpaperFetcherDelegateImpl>());
   return new ash::personalization_app::PersonalizationAppUI(
       web_ui, std::move(ambient_provider),
       std::move(keyboard_backlight_provider), std::move(theme_provider),
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
index eb05d4c..b325f44d 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
@@ -42,7 +42,7 @@
 #include "base/task/thread_pool.h"
 #include "base/unguessable_token.h"
 #include "chrome/browser/ash/wallpaper/wallpaper_enumerator.h"
-#include "chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h"
 #include "chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h"
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_manager.h"
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_manager_factory.h"
@@ -128,11 +128,11 @@
 PersonalizationAppWallpaperProviderImpl::
     PersonalizationAppWallpaperProviderImpl(
         content::WebUI* web_ui,
-        std::unique_ptr<wallpaper_handlers::BackdropFetcherDelegate>
-            backdrop_fetcher_delegate)
+        std::unique_ptr<wallpaper_handlers::WallpaperFetcherDelegate>
+            wallpaper_fetcher_delegate)
     : web_ui_(web_ui),
       profile_(Profile::FromWebUI(web_ui)),
-      backdrop_fetcher_delegate_(std::move(backdrop_fetcher_delegate)) {
+      wallpaper_fetcher_delegate_(std::move(wallpaper_fetcher_delegate)) {
   content::URLDataSource::Add(profile_,
                               std::make_unique<SanitizedImageSource>(profile_));
 }
@@ -222,7 +222,7 @@
   }
 
   wallpaper_collection_info_fetcher_ =
-      backdrop_fetcher_delegate_->CreateBackdropCollectionInfoFetcher();
+      wallpaper_fetcher_delegate_->CreateBackdropCollectionInfoFetcher();
 
   // base::Unretained is safe to use because |this| outlives
   // |wallpaper_collection_info_fetcher_|.
@@ -235,7 +235,8 @@
     const std::string& collection_id,
     FetchImagesForCollectionCallback callback) {
   auto wallpaper_images_info_fetcher =
-      backdrop_fetcher_delegate_->CreateBackdropImageInfoFetcher(collection_id);
+      wallpaper_fetcher_delegate_->CreateBackdropImageInfoFetcher(
+          collection_id);
 
   auto* wallpaper_images_info_fetcher_ptr = wallpaper_images_info_fetcher.get();
   wallpaper_images_info_fetcher_ptr->Start(base::BindOnce(
@@ -997,7 +998,7 @@
 
   std::size_t current_index = 0;
   wallpaper_attribution_info_fetcher_ =
-      backdrop_fetcher_delegate_->CreateBackdropImageInfoFetcher(
+      wallpaper_fetcher_delegate_->CreateBackdropImageInfoFetcher(
           collections->at(current_index).collection_id());
 
   wallpaper_attribution_info_fetcher_->Start(base::BindOnce(
@@ -1055,7 +1056,7 @@
     return;
   }
 
-  auto fetcher = backdrop_fetcher_delegate_->CreateBackdropImageInfoFetcher(
+  auto fetcher = wallpaper_fetcher_delegate_->CreateBackdropImageInfoFetcher(
       collections->at(current_index).collection_id());
   fetcher->Start(base::BindOnce(
       &PersonalizationAppWallpaperProviderImpl::FindImageMetadataInCollection,
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h
index 1af69214..4c985f5 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.h
@@ -22,7 +22,7 @@
 #include "base/files/file.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
-#include "chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -46,7 +46,7 @@
 
 namespace wallpaper_handlers {
 class BackdropCollectionInfoFetcher;
-class BackdropFetcherDelegate;
+class WallpaperFetcherDelegate;
 class BackdropImageInfoFetcher;
 class GooglePhotosAlbumsFetcher;
 class GooglePhotosSharedAlbumsFetcher;
@@ -69,8 +69,8 @@
  public:
   PersonalizationAppWallpaperProviderImpl(
       content::WebUI* web_ui,
-      std::unique_ptr<wallpaper_handlers::BackdropFetcherDelegate>
-          backdrop_fetcher_delegate);
+      std::unique_ptr<wallpaper_handlers::WallpaperFetcherDelegate>
+          wallpaper_fetcher_delegate);
 
   PersonalizationAppWallpaperProviderImpl(
       const PersonalizationAppWallpaperProviderImpl&) = delete;
@@ -380,8 +380,8 @@
                           ash::WallpaperControllerObserver>
       wallpaper_controller_observer_{this};
 
-  const std::unique_ptr<wallpaper_handlers::BackdropFetcherDelegate>
-      backdrop_fetcher_delegate_;
+  const std::unique_ptr<wallpaper_handlers::WallpaperFetcherDelegate>
+      wallpaper_fetcher_delegate_;
 
   // Place near bottom of class so this is cleaned up before any pending
   // callbacks are dropped.
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
index 78bcb37e..4fe9b5c 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
@@ -32,7 +32,7 @@
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
 #include "chrome/browser/ash/wallpaper_handlers/mock_wallpaper_handlers.h"
-#include "chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
 #include "chrome/browser/ash/web_applications/personalization_app/mock_personalization_app_manager.h"
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_manager_factory.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
@@ -244,7 +244,7 @@
   void SetUp() override {
     wallpaper_controller_client_ = std::make_unique<
         WallpaperControllerClientImpl>(
-        std::make_unique<wallpaper_handlers::TestBackdropFetcherDelegate>());
+        std::make_unique<wallpaper_handlers::TestWallpaperFetcherDelegate>());
     wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
 
     ASSERT_TRUE(profile_manager_.SetUp());
@@ -264,7 +264,7 @@
     wallpaper_provider_ = std::make_unique<
         PersonalizationAppWallpaperProviderImpl>(
         &web_ui_,
-        std::make_unique<wallpaper_handlers::TestBackdropFetcherDelegate>());
+        std::make_unique<wallpaper_handlers::TestWallpaperFetcherDelegate>());
 
     wallpaper_provider_->BindInterface(
         wallpaper_provider_remote_.BindNewPipeAndPassReceiver());
diff --git a/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc
index 91007c3..adade574 100644
--- a/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc
@@ -15,19 +15,29 @@
 #include "chrome/browser/ui/ash/projector/projector_app_client_impl.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
 
 namespace {
 
 #if BUILDFLAG(ENABLE_CROS_MEDIA_APP) && BUILDFLAG(ENABLE_CROS_PROJECTOR_APP)
+
+static content::EvalJsResult EvalJsInMainFrame(content::WebContents* web_ui,
+                                               const std::string& script) {
+  // Clients of this helper all run in the same isolated world.
+  constexpr int kWorldId = 1;
+  return EvalJs(web_ui->GetPrimaryMainFrame(), script,
+                content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, kWorldId);
+}
+
 // File containing the test utility library
 constexpr base::FilePath::CharType kTestLibraryPath[] =
     FILE_PATH_LITERAL("ash/webui/system_apps/public/js/dom_testing_helpers.js");
 
-void PrepareAppForTest(content::WebContents* web_contents) {
+void PrepareAnnotatorForTest(content::WebContents* web_contents) {
   EXPECT_TRUE(WaitForLoadStop(web_contents));
   EXPECT_EQ(nullptr,
-            SandboxedWebUiAppTestBase::EvalJsInAppFrame(
-                web_contents, SandboxedWebUiAppTestBase::LoadJsTestLibrary(
+            EvalJsInMainFrame(web_contents,
+                              SandboxedWebUiAppTestBase::LoadJsTestLibrary(
                                   base::FilePath(kTestLibraryPath))));
 }
 #endif  // BUILDFLAG(ENABLE_CROS_MEDIA_APP) &&
@@ -75,7 +85,7 @@
       projector_app_client->get_annotator_handler_for_test()
           ->get_web_ui_for_test()
           ->GetWebContents();
-  PrepareAppForTest(annotator_embedder);
+  PrepareAnnotatorForTest(annotator_embedder);
 
   // Checks ink is loaded by ensuring the ink engine canvas has a non zero width
   // and height attributes (checking <canvas.width/height is insufficient since
@@ -92,9 +102,9 @@
           inkCanvas.getAttribute('width') !== '0';
       })();
     )";
-  EXPECT_EQ(true, SandboxedWebUiAppTestBase::EvalJsInAppFrame(
-                      annotator_embedder, kCheckInkLoaded)
-                      .ExtractBool());
+  EXPECT_EQ(
+      true,
+      EvalJsInMainFrame(annotator_embedder, kCheckInkLoaded).ExtractBool());
 }
 #endif  // BUILDFLAG(ENABLE_CROS_MEDIA_APP) &&
         // BUILDFLAG(ENABLE_CROS_PROJECTOR_APP)
diff --git a/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc b/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc
index 62cbe42..c7717d0 100644
--- a/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc
+++ b/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc
@@ -328,6 +328,24 @@
   }
 
  protected:
+  void CreateIframe(const GURL& url, bool browsing_topics_attribute = false) {
+    content::TestNavigationObserver nav_observer(web_contents());
+
+    ExecuteScriptAsync(web_contents(),
+                       content::JsReplace(R"(
+      {
+        const iframe = document.createElement("iframe");
+        iframe.browsingTopics = $1;
+        iframe.src = $2;
+        document.body.appendChild(iframe);
+      }
+                )",
+                                          browsing_topics_attribute, url));
+
+    nav_observer.WaitForNavigationFinished();
+    EXPECT_TRUE(nav_observer.last_navigation_succeeded());
+  }
+
   void ExpectResultTopicsEqual(
       const std::vector<TopicAndDomains>& result,
       std::vector<std::pair<Topic, std::set<HashedDomain>>> expected) {
@@ -1991,4 +2009,293 @@
   }
 }
 
+// For a page that contains a static <iframe> with a "browsingtopics"
+// attribute, the iframe navigation request should be eligible for topics.
+IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
+                       CrossOriginStaticIframeWithTopicsAttribute) {
+  base::StringPairs replacement;
+  replacement.emplace_back(std::make_pair("{{STATUS}}", "200 OK"));
+  replacement.emplace_back(std::make_pair("{{OBSERVE_BROWSING_TOPICS_HEADER}}",
+                                          "Observe-Browsing-Topics: ?1"));
+  replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
+
+  GURL subframe_url = https_server_.GetURL(
+      "a.test", net::test_server::GetFilePathWithReplacements(
+                    "/browsing_topics/"
+                    "page_with_custom_topics_header.html",
+                    replacement));
+
+  base::StringPairs topics_attribute_replacement;
+  topics_attribute_replacement.emplace_back(
+      "{{MAYBE_BROWSING_TOPICS_ATTRIBUTE}}", "browsingtopics");
+
+  topics_attribute_replacement.emplace_back("{{SRC_URL}}", subframe_url.spec());
+
+  GURL main_frame_url = https_server_.GetURL(
+      "b.test", net::test_server::GetFilePathWithReplacements(
+                    "/browsing_topics/page_with_custom_attribute_iframe.html",
+                    topics_attribute_replacement));
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  absl::optional<std::string> topics_header_value =
+      GetTopicsHeaderForRequestPath(
+          "/browsing_topics/page_with_custom_topics_header.html");
+  EXPECT_TRUE(topics_header_value);
+  EXPECT_EQ(*topics_header_value, kExpectedHeaderValueForSiteB);
+
+  // A new observation should have been recorded in addition to the pre-existing
+  // one.
+  std::vector<ApiUsageContext> api_usage_contexts =
+      content::GetBrowsingTopicsApiUsage(browsing_topics_site_data_manager());
+  EXPECT_EQ(api_usage_contexts.size(), 2u);
+  EXPECT_EQ(
+      api_usage_contexts[0].hashed_main_frame_host,
+      HashMainFrameHostForStorage(https_server_.GetURL("b.test", "/").host()));
+  EXPECT_EQ(api_usage_contexts[0].hashed_context_domain,
+            GetHashedDomain("a.test"));
+  EXPECT_EQ(api_usage_contexts[1].hashed_main_frame_host,
+            HashMainFrameHostForStorage("foo1.com"));
+  EXPECT_EQ(api_usage_contexts[1].hashed_context_domain, HashedDomain(1));
+}
+
+// For a page that contains a static <iframe> without a "browsingtopics"
+// attribute, the iframe navigation request should not be eligible for topics.
+IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
+                       CrossOriginStaticIframeWithoutTopicsAttribute) {
+  base::StringPairs replacement;
+  replacement.emplace_back(std::make_pair("{{STATUS}}", "200 OK"));
+  replacement.emplace_back(std::make_pair("{{OBSERVE_BROWSING_TOPICS_HEADER}}",
+                                          "Observe-Browsing-Topics: ?1"));
+  replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
+
+  GURL subframe_url = https_server_.GetURL(
+      "a.test", net::test_server::GetFilePathWithReplacements(
+                    "/browsing_topics/"
+                    "page_with_custom_topics_header.html",
+                    replacement));
+
+  base::StringPairs topics_attribute_replacement;
+  topics_attribute_replacement.emplace_back(
+      "{{MAYBE_BROWSING_TOPICS_ATTRIBUTE}}", "");
+
+  topics_attribute_replacement.emplace_back("{{SRC_URL}}", subframe_url.spec());
+
+  GURL main_frame_url = https_server_.GetURL(
+      "b.test", net::test_server::GetFilePathWithReplacements(
+                    "/browsing_topics/page_with_custom_attribute_iframe.html",
+                    topics_attribute_replacement));
+
+  absl::optional<std::string> topics_header_value =
+      GetTopicsHeaderForRequestPath(
+          "/browsing_topics/page_with_custom_topics_header.html");
+  EXPECT_FALSE(topics_header_value);
+
+  // Since the request wasn't eligible for topics, no observation should have
+  // been recorded in addition to the pre-existing one, even though the response
+  // contains a `Observe-Browsing-Topics: ?1` header.
+  std::vector<ApiUsageContext> api_usage_contexts =
+      content::GetBrowsingTopicsApiUsage(browsing_topics_site_data_manager());
+  EXPECT_EQ(api_usage_contexts.size(), 1u);
+}
+
+// For a page with a dynamically appended iframe with iframe.browsingTopics set
+// to true, the iframe navigation request should be eligible for topics.
+IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
+                       CrossOriginDynamicIframeWithTopicsAttribute) {
+  GURL main_frame_url =
+      https_server_.GetURL("b.test", "/browsing_topics/empty_page.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  base::StringPairs replacement;
+  replacement.emplace_back(std::make_pair("{{STATUS}}", "200 OK"));
+  replacement.emplace_back(std::make_pair("{{OBSERVE_BROWSING_TOPICS_HEADER}}",
+                                          "Observe-Browsing-Topics: ?1"));
+  replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
+
+  GURL subframe_url = https_server_.GetURL(
+      "a.test", net::test_server::GetFilePathWithReplacements(
+                    "/browsing_topics/"
+                    "page_with_custom_topics_header.html",
+                    replacement));
+
+  CreateIframe(subframe_url, /*browsing_topics_attribute=*/true);
+
+  absl::optional<std::string> topics_header_value =
+      GetTopicsHeaderForRequestPath(
+          "/browsing_topics/page_with_custom_topics_header.html");
+  EXPECT_TRUE(topics_header_value);
+  EXPECT_EQ(*topics_header_value, kExpectedHeaderValueForSiteB);
+
+  // A new observation should have been recorded in addition to the pre-existing
+  // one.
+  std::vector<ApiUsageContext> api_usage_contexts =
+      content::GetBrowsingTopicsApiUsage(browsing_topics_site_data_manager());
+  EXPECT_EQ(api_usage_contexts.size(), 2u);
+  EXPECT_EQ(
+      api_usage_contexts[0].hashed_main_frame_host,
+      HashMainFrameHostForStorage(https_server_.GetURL("b.test", "/").host()));
+  EXPECT_EQ(api_usage_contexts[0].hashed_context_domain,
+            GetHashedDomain("a.test"));
+  EXPECT_EQ(api_usage_contexts[1].hashed_main_frame_host,
+            HashMainFrameHostForStorage("foo1.com"));
+  EXPECT_EQ(api_usage_contexts[1].hashed_context_domain, HashedDomain(1));
+}
+
+// For a page with a dynamically appended iframe with iframe.browsingTopics set
+// to true, the iframe navigation request should not be eligible for topics.
+IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
+                       CrossOriginDynamicIframeWithoutTopicsAttribute) {
+  GURL main_frame_url =
+      https_server_.GetURL("b.test", "/browsing_topics/empty_page.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  base::StringPairs replacement;
+  replacement.emplace_back(std::make_pair("{{STATUS}}", "200 OK"));
+  replacement.emplace_back(std::make_pair("{{OBSERVE_BROWSING_TOPICS_HEADER}}",
+                                          "Observe-Browsing-Topics: ?1"));
+  replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
+
+  GURL subframe_url = https_server_.GetURL(
+      "a.test", net::test_server::GetFilePathWithReplacements(
+                    "/browsing_topics/"
+                    "page_with_custom_topics_header.html",
+                    replacement));
+
+  CreateIframe(subframe_url);
+
+  absl::optional<std::string> topics_header_value =
+      GetTopicsHeaderForRequestPath(
+          "/browsing_topics/page_with_custom_topics_header.html");
+  EXPECT_FALSE(topics_header_value);
+
+  // Since the request wasn't eligible for topics, no observation should have
+  // been recorded in addition to the pre-existing one, even though the response
+  // contains a `Observe-Browsing-Topics: ?1` header.
+  std::vector<ApiUsageContext> api_usage_contexts =
+      content::GetBrowsingTopicsApiUsage(browsing_topics_site_data_manager());
+  EXPECT_EQ(api_usage_contexts.size(), 1u);
+}
+
+// Only allow topics from origin c.test, and test <iframe browsingtopics>
+// requests to b.test and c.test to verify that only c.test gets the header.
+IN_PROC_BROWSER_TEST_F(
+    BrowsingTopicsBrowserTest,
+    CrossOriginIframe_TopicsNotEligibleDueToPermissionsPolicyAgainstRequestOrigin) {
+  base::StringPairs allowed_origin_replacement;
+  allowed_origin_replacement.emplace_back(
+      "{{ALLOWED_ORIGIN}}", https_server_.GetOrigin("c.test").Serialize());
+
+  GURL main_frame_url = https_server_.GetURL(
+      "a.test", net::test_server::GetFilePathWithReplacements(
+                    "/browsing_topics/"
+                    "one_iframe_page_browsing_topics_allow_certain_origin.html",
+                    allowed_origin_replacement));
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  {
+    GURL subframe_url =
+        https_server_.GetURL("b.test", "/browsing_topics/empty_page.html");
+
+    CreateIframe(subframe_url, /*browsing_topics_attribute=*/true);
+
+    // No topics header was sent, as the permissions policy denied it.
+    absl::optional<std::string> topics_header_value =
+        GetTopicsHeaderForRequestPath("/browsing_topics/empty_page.html");
+    EXPECT_FALSE(topics_header_value);
+  }
+
+  {
+    GURL subframe_url =
+        https_server_.GetURL("c.test", "/browsing_topics/empty_page.html");
+
+    CreateIframe(subframe_url, /*browsing_topics_attribute=*/true);
+
+    absl::optional<std::string> topics_header_value =
+        GetTopicsHeaderForRequestPath("/browsing_topics/empty_page.html");
+    EXPECT_TRUE(topics_header_value);
+  }
+}
+
+// On site b.test, test <iframe browsingtopics> request to a.test that gets
+// redirected to c.test. The topics header should be calculated for them
+// individually (i.e. given that only a.test has observed the candidate topics
+// for site b.test, the request to a.test should have a non-empty topics header,
+// while the redirected request to c.test should have an empty topics header.)
+IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
+                       CrossOriginIframeWithRedirect) {
+  GURL main_frame_url =
+      https_server_.GetURL("b.test", "/browsing_topics/empty_page.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  base::StringPairs redirect_replacement;
+  redirect_replacement.emplace_back(std::make_pair("{{STATUS}}", "200 OK"));
+  redirect_replacement.emplace_back(std::make_pair(
+      "{{OBSERVE_BROWSING_TOPICS_HEADER}}", "Observe-Browsing-Topics: ?1"));
+  redirect_replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
+
+  GURL redirect_url = https_server_.GetURL(
+      "c.test", net::test_server::GetFilePathWithReplacements(
+                    "/browsing_topics/"
+                    "page_with_custom_topics_header2.html",
+                    redirect_replacement));
+
+  base::StringPairs replacement;
+  replacement.emplace_back(
+      std::make_pair("{{STATUS}}", "301 Moved Permanently"));
+  replacement.emplace_back(std::make_pair("{{OBSERVE_BROWSING_TOPICS_HEADER}}",
+                                          "Observe-Browsing-Topics: ?1"));
+  replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}",
+                                          "Location: " + redirect_url.spec()));
+
+  GURL subframe_url = https_server_.GetURL(
+      "a.test", net::test_server::GetFilePathWithReplacements(
+                    "/browsing_topics/"
+                    "page_with_custom_topics_header.html",
+                    replacement));
+
+  CreateIframe(subframe_url, /*browsing_topics_attribute=*/true);
+
+  {
+    absl::optional<std::string> topics_header_value =
+        GetTopicsHeaderForRequestPath(
+            "/browsing_topics/page_with_custom_topics_header.html");
+    EXPECT_TRUE(topics_header_value);
+    EXPECT_EQ(*topics_header_value, kExpectedHeaderValueForSiteB);
+  }
+  {
+    absl::optional<std::string> topics_header_value =
+        GetTopicsHeaderForRequestPath(
+            "/browsing_topics/page_with_custom_topics_header2.html");
+    EXPECT_TRUE(topics_header_value);
+
+    // An empty topics header value was sent, because "c.test" did not observe
+    // the candidate topics.
+    EXPECT_TRUE(topics_header_value->empty());
+  }
+
+  // Two new observations should have been recorded in addition to the
+  // pre-existing one.
+  std::vector<ApiUsageContext> api_usage_contexts =
+      content::GetBrowsingTopicsApiUsage(browsing_topics_site_data_manager());
+  EXPECT_EQ(api_usage_contexts.size(), 3u);
+  EXPECT_EQ(
+      api_usage_contexts[0].hashed_main_frame_host,
+      HashMainFrameHostForStorage(https_server_.GetURL("b.test", "/").host()));
+  EXPECT_EQ(api_usage_contexts[0].hashed_context_domain,
+            GetHashedDomain("c.test"));
+  EXPECT_EQ(
+      api_usage_contexts[1].hashed_main_frame_host,
+      HashMainFrameHostForStorage(https_server_.GetURL("b.test", "/").host()));
+  EXPECT_EQ(api_usage_contexts[1].hashed_context_domain,
+            GetHashedDomain("a.test"));
+  EXPECT_EQ(api_usage_contexts[2].hashed_main_frame_host,
+            HashMainFrameHostForStorage("foo1.com"));
+  EXPECT_EQ(api_usage_contexts[2].hashed_context_domain, HashedDomain(1));
+}
+
 }  // namespace browsing_topics
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index cc05624..a5fe73d 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -248,8 +248,8 @@
 #include "ash/webui/personalization_app/personalization_app_ui.h"
 #include "ash/webui/personalization_app/search/search.mojom.h"
 #include "ash/webui/print_management/print_management_ui.h"
-#include "ash/webui/projector_app/mojom/annotator.mojom.h"
-#include "ash/webui/projector_app/trusted_projector_annotator_ui.h"
+#include "ash/webui/projector_app/mojom/untrusted_annotator.mojom.h"
+#include "ash/webui/projector_app/untrusted_projector_annotator_ui.h"
 #include "ash/webui/scanning/mojom/scanning.mojom.h"
 #include "ash/webui/scanning/scanning_ui.h"
 #include "ash/webui/shimless_rma/shimless_rma.h"
@@ -1206,10 +1206,6 @@
       ash::camera_app::mojom::CameraAppHelper, ash::CameraAppUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
-      ash::annotator::mojom::AnnotatorPageHandlerFactory,
-      ash::TrustedProjectorAnnotatorUI>(map);
-
-  RegisterWebUIControllerInterfaceBinder<
       ash::help_app::mojom::PageHandlerFactory, ash::HelpAppUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
@@ -1467,6 +1463,9 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   registry.ForWebUI<ash::DemoModeAppUntrustedUI>()
       .Add<ash::mojom::demo_mode::UntrustedPageHandlerFactory>();
+
+  registry.ForWebUI<ash::UntrustedProjectorAnnotatorUI>()
+      .Add<ash::annotator::mojom::UntrustedAnnotatorPageHandlerFactory>();
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OFFICIAL_BUILD)
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 0bf0370..bdef817 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -13,6 +13,7 @@
 
 #include "base/base_switches.h"
 #include "base/command_line.h"
+#include "base/containers/fixed_flat_set.h"
 #include "base/dcheck_is_on.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
@@ -1114,7 +1115,19 @@
          process_map->Contains(extension->id(), render_process_id);
 }
 
-#endif
+// Returns true if |extension_id| is allowed to run as an Isolated Context,
+// giving it access to additional APIs.
+bool IsExtensionIdAllowedToUseIsolatedContext(base::StringPiece extension_id) {
+  static constexpr auto kAllowedIsolatedContextExtensionIds =
+      base::MakeFixedFlatSet<base::StringPiece>({
+          "algkcnfjnajfhgimadimbjhmpaeohhln",  // Secure Shell Extension (dev)
+          "iodihamcpbpeioajjeobimgagajmlibd",  // Secure Shell Extension
+                                               // (stable)
+      });
+  return base::Contains(kAllowedIsolatedContextExtensionIds, extension_id);
+}
+
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 mojo::PendingRemote<prerender::mojom::PrerenderCanceler> GetPrerenderCanceler(
     base::OnceCallback<content::WebContents*()> wc_getter) {
@@ -2493,7 +2506,9 @@
   auto* extension = extensions::ExtensionRegistry::Get(browser_context)
                         ->enabled_extensions()
                         .GetExtensionOrAppByURL(lock_url);
-  return extension && extension->is_platform_app();
+  return extension &&
+         (extension->is_platform_app() ||
+          IsExtensionIdAllowedToUseIsolatedContext(extension->id()));
 #else
   return false;
 #endif
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc
index b1faa9db..8d8bc53fb 100644
--- a/chrome/browser/devtools/devtools_browsertest.cc
+++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -1769,7 +1769,7 @@
 
 class DevToolsExtensionFileAccessTest : public DevToolsExtensionTest {
  protected:
-  void Run(bool allow_file_access) {
+  void Run(bool allow_file_access, const std::string& url_scheme) {
     extensions::TestExtensionDir dir;
 
     dir.WriteManifest(BuildExtensionManifest("File Access", "devtools.html"));
@@ -1793,28 +1793,36 @@
                 .AppendASCII("content/test/data/devtools/navigation.html"))
             .spec();
 
-    base::ReplaceFirstSubstringAfterOffset(&file_url, 0, "file:///", "file:");
-
     const Extension* extension =
         LoadExtensionFromPath(dir.UnpackedPath(), allow_file_access);
     ASSERT_TRUE(extension);
 
     std::string url = base::StringPrintf(
-        R"(data:text/html,<script>//%%23%%20sourceMappingURL=data:application/json,{"version":3,"sources":["file:%s"]}</script>)",
-        file_url.c_str());
+        R"(data:text/html,<script>//%%23%%20sourceMappingURL=data:application/json,{"version":3,"sources":["%s:%s"]}</script>)",
+        url_scheme.c_str(), file_url.c_str() + strlen("file:///"));
     OpenDevToolsWindow(url, false);
     RunTestFunction(window_, "waitForTestResultsAsMessage");
   }
 };
 
 IN_PROC_BROWSER_TEST_F(DevToolsExtensionFileAccessTest,
-                       CantGetFileResourceWithoutFileAccess) {
-  Run(false);
+                       CanGetFileResourceWithFileAccess) {
+  Run(true, "file:///");
 }
 
 IN_PROC_BROWSER_TEST_F(DevToolsExtensionFileAccessTest,
-                       CanGetFileResourceWithFileAccess) {
-  Run(true);
+                       CantGetFileResourceWithoutFileAccess) {
+  Run(false, "file:///");
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionFileAccessTest,
+                       CantGetFileResourceWithoutFileAccessNoSlashes) {
+  Run(false, "file:");
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionFileAccessTest,
+                       CantGetFileResourceWithoutFileAccessMixedCase) {
+  Run(false, "fILe:");
 }
 
 // Tests that scripts are not duplicated after Scripts Panel switch.
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
index 6162047f..215c183 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
@@ -540,7 +540,7 @@
     // MultiFileRequestHandler is owned by this class.
     files_request_handler_ = FilesRequestHandler::Create(
         GetBinaryUploadService(), profile_, data_.settings, url_, "", "",
-        user_action_id_, access_point_, data_.paths,
+        user_action_id_, title_, access_point_, data_.paths,
         base::BindOnce(&ContentAnalysisDelegate::FilesRequestCallback,
                        GetWeakPtr()));
     files_request_complete_ = !files_request_handler_->UploadData();
diff --git a/chrome/browser/enterprise/connectors/analysis/fake_files_request_handler.cc b/chrome/browser/enterprise/connectors/analysis/fake_files_request_handler.cc
index 73b31e6..a26eb67 100644
--- a/chrome/browser/enterprise/connectors/analysis/fake_files_request_handler.cc
+++ b/chrome/browser/enterprise/connectors/analysis/fake_files_request_handler.cc
@@ -17,6 +17,7 @@
     const std::string& source,
     const std::string& destination,
     const std::string& user_action_id,
+    const std::string& tab_title,
     safe_browsing::DeepScanAccessPoint access_point,
     const std::vector<base::FilePath>& paths,
     CompletionCallback callback)
@@ -27,6 +28,7 @@
                                                  source,
                                                  destination,
                                                  user_action_id,
+                                                 tab_title,
                                                  access_point,
                                                  paths,
                                                  std::move(callback)),
@@ -45,12 +47,13 @@
     const std::string& source,
     const std::string& destination,
     const std::string& user_action_id,
+    const std::string& tab_title,
     safe_browsing::DeepScanAccessPoint access_point,
     const std::vector<base::FilePath>& paths,
     enterprise_connectors::FilesRequestHandler::CompletionCallback callback) {
   return std::make_unique<FakeFilesRequestHandler>(
       fake_file_upload_callback, upload_service, profile, analysis_settings,
-      url, source, destination, user_action_id, access_point, paths,
+      url, source, destination, user_action_id, tab_title, access_point, paths,
       std::move(callback));
 }
 
diff --git a/chrome/browser/enterprise/connectors/analysis/fake_files_request_handler.h b/chrome/browser/enterprise/connectors/analysis/fake_files_request_handler.h
index 1f1bd632..2cc129f 100644
--- a/chrome/browser/enterprise/connectors/analysis/fake_files_request_handler.h
+++ b/chrome/browser/enterprise/connectors/analysis/fake_files_request_handler.h
@@ -33,6 +33,7 @@
       const std::string& source,
       const std::string& destination,
       const std::string& user_action_id,
+      const std::string& tab_title,
       safe_browsing::DeepScanAccessPoint access_point,
       const std::vector<base::FilePath>& paths,
       CompletionCallback callback);
@@ -48,6 +49,7 @@
       const std::string& source,
       const std::string& destination,
       const std::string& user_action_id,
+      const std::string& tab_title,
       safe_browsing::DeepScanAccessPoint access_point,
       const std::vector<base::FilePath>& paths,
       enterprise_connectors::FilesRequestHandler::CompletionCallback callback);
diff --git a/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc
index 99b73d6..6147150 100644
--- a/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc
+++ b/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc
@@ -342,9 +342,10 @@
           profile_, source_url_.path()),
       SourceDestinationMatcherAsh::GetVolumeDescriptionFromPath(
           profile_, destination_url_.path()),
-      // User action id is only needed for local content analysis, leave it
-      // empty here.
-      /*user_action_id=*/std::string(), access_point_, std::move(paths),
+      // User action id and tab title are only needed for local content
+      // analysis, leave them empty here.
+      /*user_action_id=*/std::string(), /*tab_title=*/std::string(),
+      access_point_, std::move(paths),
       base::BindOnce(&FileTransferAnalysisDelegate::ContentAnalysisCompleted,
                      weak_ptr_factory_.GetWeakPtr()));
   request_handler_->UploadData();
diff --git a/chrome/browser/enterprise/connectors/analysis/files_request_handler.cc b/chrome/browser/enterprise/connectors/analysis/files_request_handler.cc
index 8148900..175daab 100644
--- a/chrome/browser/enterprise/connectors/analysis/files_request_handler.cc
+++ b/chrome/browser/enterprise/connectors/analysis/files_request_handler.cc
@@ -77,6 +77,7 @@
     const std::string& source,
     const std::string& destination,
     const std::string& user_action_id,
+    const std::string& tab_title,
     safe_browsing::DeepScanAccessPoint access_point,
     const std::vector<base::FilePath>& paths,
     CompletionCallback callback)
@@ -87,6 +88,7 @@
                          source,
                          destination,
                          user_action_id,
+                         tab_title,
                          paths.size(),
                          access_point),
       paths_(paths),
@@ -105,18 +107,19 @@
     const std::string& source,
     const std::string& destination,
     const std::string& user_action_id,
+    const std::string& tab_title,
     safe_browsing::DeepScanAccessPoint access_point,
     const std::vector<base::FilePath>& paths,
     CompletionCallback callback) {
   if (GetFactoryStorage()->is_null()) {
     return base::WrapUnique(new FilesRequestHandler(
         upload_service, profile, analysis_settings, url, source, destination,
-        user_action_id, access_point, paths, std::move(callback)));
+        user_action_id, tab_title, access_point, paths, std::move(callback)));
   } else {
     // Use the factory to create a fake FilesRequestHandler.
-    return GetFactoryStorage()->Run(upload_service, profile, analysis_settings,
-                                    url, source, destination, user_action_id,
-                                    access_point, paths, std::move(callback));
+    return GetFactoryStorage()->Run(
+        upload_service, profile, analysis_settings, url, source, destination,
+        user_action_id, tab_title, access_point, paths, std::move(callback));
   }
 }
 
diff --git a/chrome/browser/enterprise/connectors/analysis/files_request_handler.h b/chrome/browser/enterprise/connectors/analysis/files_request_handler.h
index 61fc6bf..539b8e1 100644
--- a/chrome/browser/enterprise/connectors/analysis/files_request_handler.h
+++ b/chrome/browser/enterprise/connectors/analysis/files_request_handler.h
@@ -59,6 +59,7 @@
       const std::string& source,
       const std::string& destination,
       const std::string& user_action_id,
+      const std::string& tab_title,
       safe_browsing::DeepScanAccessPoint access_point,
       const std::vector<base::FilePath>& paths,
       CompletionCallback callback)>;
@@ -76,6 +77,7 @@
       const std::string& source,
       const std::string& destination,
       const std::string& user_action_id,
+      const std::string& tab_title,
       safe_browsing::DeepScanAccessPoint access_point,
       const std::vector<base::FilePath>& paths,
       CompletionCallback callback);
@@ -98,6 +100,7 @@
       const std::string& source,
       const std::string& destination,
       const std::string& user_action_id,
+      const std::string& tab_title,
       safe_browsing::DeepScanAccessPoint access_point,
       const std::vector<base::FilePath>& paths,
       CompletionCallback callback);
diff --git a/chrome/browser/enterprise/connectors/analysis/files_request_handler_unittest.cc b/chrome/browser/enterprise/connectors/analysis/files_request_handler_unittest.cc
index da6d3c8..c1a30a0 100644
--- a/chrome/browser/enterprise/connectors/analysis/files_request_handler_unittest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/files_request_handler_unittest.cc
@@ -45,6 +45,7 @@
 
 constexpr char kDmToken[] = "dm_token";
 constexpr char kUserActionId[] = "123";
+constexpr char kTabTitle[] = "tab_title";
 constexpr char kTestUrl[] = "http://example.com/";
 base::TimeDelta kResponseDelay = base::Seconds(0);
 
@@ -204,8 +205,8 @@
             weak_ptr_factory_.GetWeakPtr(),
             settings.cloud_or_local_settings.is_cloud_analysis()),
         /*upload_service=*/nullptr, profile_, settings, GURL(kTestUrl), "", "",
-        kUserActionId, safe_browsing::DeepScanAccessPoint::UPLOAD, paths,
-        future.GetCallback());
+        kUserActionId, kTabTitle, safe_browsing::DeepScanAccessPoint::UPLOAD,
+        paths, future.GetCallback());
 
     fake_files_request_handler_->UploadData();
 
@@ -296,6 +297,7 @@
       EXPECT_EQ(request->user_action_requests_count(),
                 expected_user_action_requests_count_);
       EXPECT_EQ(request->user_action_id(), kUserActionId);
+      EXPECT_EQ(request->tab_title(), kTabTitle);
     }
 
     // Simulate a response.
diff --git a/chrome/browser/enterprise/connectors/analysis/request_handler_base.cc b/chrome/browser/enterprise/connectors/analysis/request_handler_base.cc
index 00b47c3..fdafb4b 100644
--- a/chrome/browser/enterprise/connectors/analysis/request_handler_base.cc
+++ b/chrome/browser/enterprise/connectors/analysis/request_handler_base.cc
@@ -15,6 +15,7 @@
     const std::string& source,
     const std::string& destination,
     const std::string& user_action_id,
+    const std::string& tab_title,
     uint64_t user_action_requests_count,
     safe_browsing::DeepScanAccessPoint access_point)
     : upload_service_(upload_service ? upload_service->AsWeakPtr() : nullptr),
@@ -24,6 +25,7 @@
       source_(source),
       destination_(destination),
       user_action_id_(user_action_id),
+      tab_title_(tab_title),
       user_action_requests_count_(user_action_requests_count),
       access_point_(access_point) {}
 
@@ -55,6 +57,7 @@
   if (analysis_settings_->cloud_or_local_settings.is_local_analysis()) {
     request->set_user_action_id(user_action_id_);
     request->set_user_action_requests_count(user_action_requests_count_);
+    request->set_tab_title(tab_title_);
   }
 
   request->set_analysis_connector(connector);
diff --git a/chrome/browser/enterprise/connectors/analysis/request_handler_base.h b/chrome/browser/enterprise/connectors/analysis/request_handler_base.h
index 0939abb..47bc824b 100644
--- a/chrome/browser/enterprise/connectors/analysis/request_handler_base.h
+++ b/chrome/browser/enterprise/connectors/analysis/request_handler_base.h
@@ -39,6 +39,7 @@
       const std::string& source,
       const std::string& destination,
       const std::string& user_action_id,
+      const std::string& tab_title,
       uint64_t user_action_requests_count,
       safe_browsing::DeepScanAccessPoint access_point);
 
@@ -91,6 +92,7 @@
   std::string source_;
   std::string destination_;
   std::string user_action_id_;
+  std::string tab_title_;
   uint64_t user_action_requests_count_;
   safe_browsing::DeepScanAccessPoint access_point_;
 
diff --git a/chrome/browser/extensions/api/runtime/runtime_apitest.cc b/chrome/browser/extensions/api/runtime/runtime_apitest.cc
index de87c56..844e984e 100644
--- a/chrome/browser/extensions/api/runtime/runtime_apitest.cc
+++ b/chrome/browser/extensions/api/runtime/runtime_apitest.cc
@@ -8,6 +8,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/simple_test_tick_clock.h"
+#include "base/test/values_test_util.h"
 #include "base/time/time.h"
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #include "chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h"
@@ -15,10 +16,12 @@
 #include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/version_info/channel.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "extensions/browser/api/runtime/runtime_api.h"
+#include "extensions/browser/background_script_executor.h"
 #include "extensions/browser/blocklist_extension_prefs.h"
 #include "extensions/browser/blocklist_state.h"
 #include "extensions/browser/extension_dialog_auto_confirm.h"
@@ -26,6 +29,8 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/test_extension_registry_observer.h"
+#include "extensions/common/extension_features.h"
+#include "extensions/common/features/feature_channel.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 #include "extensions/test/test_extension_dir.h"
@@ -598,4 +603,77 @@
   }
 }
 
+class RuntimeGetContextsApiTest : public ExtensionApiTest {
+ public:
+  RuntimeGetContextsApiTest() {
+    feature_list_.InitAndEnableFeature(
+        extensions_features::kApiRuntimeGetContexts);
+  }
+  RuntimeGetContextsApiTest(const RuntimeGetContextsApiTest&) = delete;
+  RuntimeGetContextsApiTest& operator=(const RuntimeGetContextsApiTest&) =
+      delete;
+  ~RuntimeGetContextsApiTest() override = default;
+
+  void SetUpOnMainThread() override {
+    ExtensionApiTest::SetUpOnMainThread();
+
+    static constexpr char kManifest[] =
+        R"({
+             "name": "Get Contexts",
+             "version": "0.1",
+             "manifest_version": 3,
+             "background": {
+               "service_worker": "background.js"
+             }
+           })";
+    test_dir_.WriteManifest(kManifest);
+    test_dir_.WriteFile(FILE_PATH_LITERAL("background.js"),
+                        "// Intentionally blank");
+    extension_ = LoadExtension(test_dir_.UnpackedPath());
+    ASSERT_TRUE(extension_);
+  }
+
+  // Runs `chrome.runtime.getContexts()` and returns the result as a
+  // base::Value.
+  base::Value GetContexts() {
+    static constexpr char kScript[] =
+        R"((async () => {
+             chrome.test.sendScriptResult(
+                 await chrome.runtime.getContexts());
+           })();)";
+    return BackgroundScriptExecutor::ExecuteScript(
+        profile(), extension_->id(), kScript,
+        BackgroundScriptExecutor::ResultCapture::kSendScriptResult);
+  }
+
+ private:
+  const Extension* extension_ = nullptr;
+  TestExtensionDir test_dir_;
+  ScopedCurrentChannel channel_override_{version_info::Channel::UNKNOWN};
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Tests retrieving the background service worker context using
+// `chrome.runtime.getContexts()`.
+IN_PROC_BROWSER_TEST_F(RuntimeGetContextsApiTest, GetServiceWorkerContext) {
+  base::Value background_contexts = GetContexts();
+  // Note: fields of `documentId`, `documentUrl`, and `documentOrigin` are
+  // undefined (service worker contexts don't have an associated document).
+  // `tabId`, `frameId`, and `windowId` are -1 for consistency with other
+  // APIs.
+  static constexpr char kExpected[] =
+      R"([{
+            "contextType": "BACKGROUND",
+            "contextId": "",
+            "tabId": -1,
+            "windowId": -1,
+            "frameId": -1,
+            "incognito": false
+         }])";
+  EXPECT_THAT(background_contexts, base::test::IsJson(kExpected));
+
+  // TODO(crbug/1426192): Add tests for retrieving a service worker context
+  // when there isn't an active worker.
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index 20ff4e9f..f8990a0 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -343,9 +343,8 @@
   base::Value ExecuteScriptInBackgroundPage(
       const std::string& extension_id,
       const std::string& script,
-      extensions::browsertest_util::ScriptUserActivation
-          script_user_activation =
-              extensions::browsertest_util::ScriptUserActivation::kActivate);
+      browsertest_util::ScriptUserActivation script_user_activation =
+          browsertest_util::ScriptUserActivation::kDontActivate);
 
   // Waits until |script| calls "window.domAutomationController.send(result)",
   // where |result| is a string, and returns |result|. Fails the test and
@@ -356,9 +355,8 @@
   std::string ExecuteScriptInBackgroundPageDeprecated(
       const std::string& extension_id,
       const std::string& script,
-      extensions::browsertest_util::ScriptUserActivation
-          script_user_activation =
-              extensions::browsertest_util::ScriptUserActivation::kActivate);
+      browsertest_util::ScriptUserActivation script_user_activation =
+          browsertest_util::ScriptUserActivation::kDontActivate);
 
   bool ExecuteScriptInBackgroundPageNoWait(const std::string& extension_id,
                                            const std::string& script);
diff --git a/chrome/browser/extensions/offscreen_document_browsertest.cc b/chrome/browser/extensions/offscreen_document_browsertest.cc
index 412d752..9775674 100644
--- a/chrome/browser/extensions/offscreen_document_browsertest.cc
+++ b/chrome/browser/extensions/offscreen_document_browsertest.cc
@@ -230,8 +230,11 @@
              domAutomationController.send(JSON.stringify(keys.sort()));
            })";
     static constexpr char kExpectedProperties[] =
-        R"(["OnInstalledReason","OnRestartRequiredReason","PlatformArch",)"
-        R"("PlatformNaclArch","PlatformOs","RequestUpdateCheckStatus",)"
+        // Enums.
+        R"(["ContextType","OnInstalledReason","OnRestartRequiredReason",)"
+        R"("PlatformArch","PlatformNaclArch","PlatformOs",)"
+        R"("RequestUpdateCheckStatus",)"
+        // Methods and events.
         R"("connect","getURL","id","onConnect","onConnectExternal",)"
         R"("onMessage","onMessageExternal","sendMessage"])";
     EXPECT_EQ(kExpectedProperties, ExecuteScriptSync(contents, kScript));
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 8f25970f..ef165e6 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -494,6 +494,11 @@
     "expiry_milestone": 120
   },
   {
+    "name": "autofill-enable-email-otp-for-vcn-yellow-path",
+     "owners": ["junhuihe@google.com"],
+     "expiry_milestone": 125
+  },
+  {
     "name": "autofill-enable-fido-progress-dialog",
     "owners": [ "siashah", "yiian" ],
     "expiry_milestone": 120
@@ -920,7 +925,7 @@
   {
     "name": "captive-portal-error-page",
     "owners": [ "stevenjb", "cros-network-health-team@google.com" ],
-    "expiry_milestone": 114
+    "expiry_milestone": 120
   },
   {
     "name": "cast-streaming-hardware-h264",
@@ -1950,7 +1955,7 @@
   {
     "name": "enable-commerce-developer",
     "owners": [ "chrome-shopping@google.com" ],
-    "expiry_milestone": 110
+    "expiry_milestone": 125
   },
   {
     "name": "enable-commerce-hint-android",
@@ -2556,17 +2561,17 @@
   {
     "name": "enable-holding-space-predictability",
     "owners": [ "//ash/public/cpp/holding_space/OWNERS" ],
-    "expiry_milestone": 114
+    "expiry_milestone": 116
   },
   {
     "name": "enable-holding-space-refresh",
     "owners": [ "//ash/public/cpp/holding_space/OWNERS" ],
-    "expiry_milestone": 114
+    "expiry_milestone": 116
   },
   {
     "name": "enable-holding-space-suggestions",
     "owners": [ "//ash/public/cpp/holding_space/OWNERS" ],
-    "expiry_milestone": 114
+    "expiry_milestone": 116
   },
   {
     "name": "enable-hostname-setting",
@@ -6485,7 +6490,7 @@
     "expiry_milestone": 96
   },
   {
-    "name": "settings-enable-get-the-most-out-of-program",
+    "name": "settings-enable-get-the-most-out-of-chrome",
     "owners": [ "jochen" ],
     "expiry_milestone": 130
   },
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 647c983..37ab5f81 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -400,6 +400,15 @@
     "When enabled, card product name (instead of issuer network) will be shown "
     "in Payments Autofill UI.";
 
+const char kAutofillEnableEmailOtpForVcnYellowPathName[] =
+    "Enable email OTP authentication in the yellow path of the VCN retrieval "
+    "flow";
+const char kAutofillEnableEmailOtpForVcnYellowPathDescription[] =
+    "When enabled, if the user encounters the yellow path (challenge path) in "
+    "the VCN retrieval flow and the server denotes that the card is eligible "
+    "for email OTP authentication, email OTP authentication will be offered as "
+    "one of the challenge options.";
+
 const char kAutofillEnableNewCardArtAndNetworkImagesName[] =
     "Enable showing new card art and network images";
 const char kAutofillEnableNewCardArtAndNetworkImagesDescription[] =
@@ -4533,11 +4542,13 @@
     "Enables SCT auditing for users who have opted in to Safe Browsing "
     "Extended Reporting.";
 
-const char kSettingsEnableGetTheMostOutOfProgramName[] =
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+const char kSettingsEnableGetTheMostOutOfChromeName[] =
     "'Get the most out of Chrome' documentation";
-const char kSettingsEnableGetTheMostOutOfProgramDescription[] =
+const char kSettingsEnableGetTheMostOutOfChromeDescription[] =
     "When enabled, the 'Get the most out of Chrome' documentation section "
     "will be available.";
+#endif
 
 const char kSharingDesktopSharePreviewName[] = "Desktop share hub preview";
 const char kSharingDesktopSharePreviewDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 1e0c244..332a2bb 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -232,6 +232,9 @@
 extern const char kAutofillEnableCardProductNameName[];
 extern const char kAutofillEnableCardProductNameDescription[];
 
+extern const char kAutofillEnableEmailOtpForVcnYellowPathName[];
+extern const char kAutofillEnableEmailOtpForVcnYellowPathDescription[];
+
 extern const char kAutofillEnableNewCardArtAndNetworkImagesName[];
 extern const char kAutofillEnableNewCardArtAndNetworkImagesDescription[];
 
@@ -2606,8 +2609,10 @@
 extern const char kSCTAuditingName[];
 extern const char kSCTAuditingDescription[];
 
-extern const char kSettingsEnableGetTheMostOutOfProgramName[];
-extern const char kSettingsEnableGetTheMostOutOfProgramDescription[];
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+extern const char kSettingsEnableGetTheMostOutOfChromeName[];
+extern const char kSettingsEnableGetTheMostOutOfChromeDescription[];
+#endif
 
 extern const char kSharingDesktopSharePreviewName[];
 extern const char kSharingDesktopSharePreviewDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index ebd0ad35..893a017 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -229,6 +229,7 @@
     &kContextualSearchThinWebViewImplementation,
     &kDeferKeepScreenOnDuringGesture,
     &kDeferNotifyInMotion,
+    &kDelayTransitionsForAnimation,
     &kExperimentsForAgsa,
     &kExploreSites,
     &kFocusOmniboxInIncognitoTabIntents,
@@ -726,6 +727,10 @@
              "DeferNotifyInMotion",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kDelayTransitionsForAnimation,
+             "DelayTransitionsForAnimation",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 BASE_FEATURE(kDownloadAutoResumptionThrottling,
              "DownloadAutoResumptionThrottling",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 82e4cde..3e90bb1 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -87,6 +87,7 @@
 BASE_DECLARE_FEATURE(kContextualSearchThinWebViewImplementation);
 BASE_DECLARE_FEATURE(kDeferKeepScreenOnDuringGesture);
 BASE_DECLARE_FEATURE(kDeferNotifyInMotion);
+BASE_DECLARE_FEATURE(kDelayTransitionsForAnimation);
 BASE_DECLARE_FEATURE(kDontPrefetchLibraries);
 BASE_DECLARE_FEATURE(kDownloadAutoResumptionThrottling);
 BASE_DECLARE_FEATURE(kDownloadHomeForExternalApp);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 79a5571a..94d0e799 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -198,6 +198,8 @@
             "AutofillEnableNewCardArtAndNetworkImages";
     public static final String AUTOFILL_ENABLE_SUPPORT_FOR_HONORIFIC_PREFIXES =
             "AutofillEnableSupportForHonorificPrefixes";
+    public static final String AUTOFILL_ENABLE_SUPPORT_FOR_MORE_STRUCTURE_IN_ADDRESSES =
+            "AutofillEnableSupportForMoreStructureInAddresses";
     public static final String AUTOFILL_ENABLE_UPDATE_VIRTUAL_CARD_ENROLLMENT =
             "AutofillEnableUpdateVirtualCardEnrollment";
     public static final String AUTOFILL_ENABLE_VIRTUAL_CARD_METADATA =
@@ -213,6 +215,8 @@
     public static final String BACK_GESTURE_REFACTOR_ACTIVITY =
             "BackGestureRefactorActivityAndroid";
     public static final String BASELINE_GM3_SURFACE_COLORS = "BaselineGM3SurfaceColors";
+    public static final String BIOMETRIC_TOUCH_TO_FILL = "BiometricTouchToFill";
+    public static final String BOOKMARKS_IMPROVED_SAVE_FLOW = "BookmarksImprovedSaveFlow";
     public static final String BOOKMARKS_REFRESH = "BookmarksRefresh";
     public static final String CACHE_DEPRECATED_SYSTEM_LOCATION_SETTING =
             "CacheDeprecatedSystemLocationSetting";
@@ -221,6 +225,7 @@
     public static final String CCT_ALLOW_CROSS_UID_ACTIVITY_SWITCH_FROM_BELOW =
             "CCTAllowCrossUidActivitySwitchFromBelow";
     public static final String CCT_AUTO_TRANSLATE = "CCTAutoTranslate";
+    public static final String CCT_BACKGROUND_TAB = "CCTBackgroundTab";
     public static final String CCT_BOTTOM_BAR_SWIPE_UP_GESTURE = "CCTBottomBarSwipeUpGesture";
     public static final String CCT_BRAND_TRANSPARENCY = "CCTBrandTransparency";
     public static final String CCT_CLIENT_DATA_HEADER = "CCTClientDataHeader";
@@ -252,6 +257,7 @@
     public static final String CCT_RESOURCE_PREFETCH = "CCTResourcePrefetch";
     public static final String CCT_RETAINING_STATE_IN_MEMORY = "CCTRetainingStateInMemory";
     public static final String CCT_TOOLBAR_CUSTOMIZATIONS = "CCTToolbarCustomizations";
+    public static final String CHROME_NEW_DOWNLOAD_TAB = "ChromeNewDownloadTab";
     public static final String CHROME_SHARING_HUB = "ChromeSharingHub";
     public static final String CHROME_SHARING_HUB_LAUNCH_ADJACENT =
             "ChromeSharingHubLaunchAdjacent";
@@ -263,6 +269,7 @@
     public static final String COMMERCE_COUPONS = "CommerceCoupons";
     public static final String COMMERCE_MERCHANT_VIEWER = "CommerceMerchantViewer";
     public static final String COMMERCE_PRICE_TRACKING = "CommercePriceTracking";
+    public static final String CONTACTS_PICKER_SELECT_ALL = "ContactsPickerSelectAll";
     public static final String CONTEXTUAL_PAGE_ACTIONS = "ContextualPageActions";
     public static final String CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING =
             "ContextualPageActionPriceTracking";
@@ -293,12 +300,17 @@
     public static final String DEFER_KEEP_SCREEN_ON_DURING_GESTURE =
             "DeferKeepScreenOnDuringGesture";
     public static final String DEFER_NOTIFY_IN_MOTION = "DeferNotifyInMotion";
+    public static final String DELAY_TRANSITIONS_FOR_ANIMATION = "DelayTransitionsForAnimation";
     public static final String DETAILED_LANGUAGE_SETTINGS = "DetailedLanguageSettings";
+    public static final String DISCO_FEED_ENDPOINT = "DiscoFeedEndpoint";
     public static final String DNS_OVER_HTTPS = "DnsOverHttps";
+    public static final String DONT_AUTO_HIDE_BROWSER_CONTROLS = "DontAutoHideBrowserControls";
+    public static final String DOWNLOADS_FOREGROUND = "DownloadsForeground";
     public static final String DOWNLOAD_OFFLINE_CONTENT_PROVIDER =
             "UseDownloadOfflineContentProvider";
     public static final String EARLY_LIBRARY_LOAD = "EarlyLibraryLoad";
     public static final String ENABLE_IPH = "EnableIPH";
+    public static final String ENABLE_PASSWORDS_ACCOUNT_STORAGE = "EnablePasswordsAccountStorage";
     public static final String EXPERIMENTS_FOR_AGSA = "ExperimentsForAgsa";
     public static final String EXPLICIT_LANGUAGE_ASK = "ExplicitLanguageAsk";
     public static final String EXPLORE_SITES = "ExploreSites";
@@ -309,10 +321,14 @@
             "FeedImageMemoryCacheSizePercentage";
     public static final String FEED_LOADING_PLACEHOLDER = "FeedLoadingPlaceholder";
     public static final String FEED_MULTI_COLUMN = "DiscoverFeedMultiColumn";
+    public static final String FEED_NO_VIEW_CACHE = "FeedNoViewCache";
+    public static final String FEED_INTERACTIVE_REFRESH = "FeedInteractiveRefresh";
+    public static final String FEED_PERFORMANCE_STUDY = "FeedPerformanceStudy";
     public static final String FEED_POSITION_ANDROID = "FeedPositionAndroid";
     public static final String FEED_SHOW_SIGN_IN_COMMAND = "FeedShowSignInCommand";
     public static final String FEED_BOC_SIGN_IN_INTERSTITIAL = "FeedBoCSigninInterstitial";
     public static final String FILLING_PASSWORDS_FROM_ANY_ORIGIN = "FillingPasswordsFromAnyOrigin";
+    public static final String FIXED_UMA_SESSION_RESUME_ORDER = "FixedUmaSessionResumeOrder";
     public static final String FOCUS_OMNIBOX_IN_INCOGNITO_TAB_INTENTS =
             "FocusOmniboxInIncognitoTabIntents";
     public static final String FOLDABLE_JANK_FIX = "FoldableJankFix";
@@ -338,6 +354,8 @@
     public static final String INSTANCE_SWITCHER = "InstanceSwitcher";
     public static final String INSTANT_START = "InstantStart";
     public static final String INTEREST_FEED_CONTENT_SUGGESTIONS = "InterestFeedContentSuggestions";
+    public static final String INTEREST_FEED_NOTICE_CARD_AUTO_DISMISS =
+            "InterestFeedNoticeCardAutoDismiss";
     public static final String INTEREST_FEED_V2 = "InterestFeedV2";
     public static final String INTEREST_FEED_V2_AUTOPLAY = "InterestFeedV2Autoplay";
     public static final String INTEREST_FEED_V2_HEARTS = "InterestFeedV2Hearts";
@@ -348,12 +366,17 @@
     public static final String LOCAL_WEB_APPROVALS = "LocalWebApprovals";
     public static final String LOOKALIKE_NAVIGATION_URL_SUGGESTIONS_UI =
             "LookalikeUrlNavigationSuggestionsUI";
+    public static final String MARK_HTTP_AS = "MarkHttpAs";
     public static final String MESSAGES_FOR_ANDROID_ADS_BLOCKED = "MessagesForAndroidAdsBlocked";
     public static final String MESSAGES_FOR_ANDROID_INFRASTRUCTURE =
             "MessagesForAndroidInfrastructure";
     public static final String MESSAGES_FOR_ANDROID_PERMISSION_UPDATE =
             "MessagesForAndroidPermissionUpdate";
+    public static final String MESSAGES_FOR_ANDROID_STACKING_ANIMATION =
+            "MessagesForAndroidStackingAnimation";
+    public static final String MESSAGES_FOR_ANDROID_SYNC_ERROR = "MessagesForAndroidSyncError";
     public static final String METRICS_SETTINGS_ANDROID = "MetricsSettingsAndroid";
+    public static final String MODAL_PERMISSION_DIALOG_VIEW = "ModalPermissionDialogView";
     public static final String NOTIFICATION_PERMISSION_VARIANT = "NotificationPermissionVariant";
     public static final String NOTIFICATION_PERMISSION_BOTTOM_SHEET =
             "NotificationPermissionBottomSheet";
@@ -383,6 +406,7 @@
             "OptimizationGuidePushNotifications";
     public static final String OPTIMIZE_GEOLOCATION_HEADER_GENERATION =
             "OptimizeGeolocationHeaderGeneration";
+    public static final String OVERLAY_NEW_LAYOUT = "OverlayNewLayout";
     public static final String PAGE_ANNOTATIONS_SERVICE = "PageAnnotationsService";
     public static final String PAGE_INFO_ABOUT_THIS_SITE_EN = "PageInfoAboutThisSiteEn";
     public static final String PAGE_INFO_ABOUT_THIS_SITE_IMPROVED_BOTTOMSHEET =
@@ -392,11 +416,15 @@
     public static final String PAGE_INFO_ABOUT_THIS_SITE_NEW_ICON = "PageInfoAboutThisSiteNewIcon";
     public static final String PAGE_INFO_ABOUT_THIS_SITE_NON_EN = "PageInfoAboutThisSiteNonEn";
     public static final String PAINT_PREVIEW_DEMO = "PaintPreviewDemo";
+    public static final String PAINT_PREVIEW_SHOW_ON_STARTUP = "PaintPreviewShowOnStartup";
     public static final String PARTNER_HOMEPAGE_INITIAL_LOAD_IMPROVEMENT =
             "PartnerHomepageInitialLoadImprovement";
     public static final String PASSKEY_MANAGEMENT_USING_ACCOUNT_SETTINGS_ANDROID =
             "PasskeyManagementUsingAccountSettingsAndroid";
+    public static final String PASSWORD_DOMAIN_CAPABILITIES_FETCHING =
+            "PasswordDomainCapabilitiesFetching";
     public static final String PASSWORD_EDIT_DIALOG_WITH_DETAILS = "PasswordEditDialogWithDetails";
+    public static final String PERMISSION_DELEGATION = "PermissionDelegation";
     public static final String PORTALS = "Portals";
     public static final String PORTALS_CROSS_ORIGIN = "PortalsCrossOrigin";
     public static final String POST_TASK_FOCUS_TAB = "PostTaskFocusTab";
@@ -429,6 +457,8 @@
     public static final String RECOVER_FROM_NEVER_SAVE_ANDROID = "RecoverFromNeverSaveAndroid";
     public static final String REENGAGEMENT_NOTIFICATION = "ReengagementNotification";
     public static final String RELATED_SEARCHES = "RelatedSearches";
+    public static final String REPORT_PARENTAL_CONTROL_SITES_CHILD =
+            "ReportParentalControlSitesChild";
     public static final String REQUEST_DESKTOP_SITE_DEFAULTS = "RequestDesktopSiteDefaults";
     public static final String REQUEST_DESKTOP_SITE_DEFAULTS_CONTROL =
             "RequestDesktopSiteDefaultsControl";
@@ -448,6 +478,7 @@
     public static final String RESIZE_ONLY_ACTIVE_TAB = "ResizeOnlyActiveTab";
     public static final String SAFE_BROWSING_DELAYED_WARNINGS = "SafeBrowsingDelayedWarnings";
     public static final String SAFE_MODE_FOR_CACHED_FLAGS = "SafeModeForCachedFlags";
+    public static final String SCREENSHOTS_FOR_ANDROID_V2 = "ScreenshotsForAndroidV2";
     public static final String SEARCH_RESUMPTION_MODULE_ANDROID = "SearchResumptionModuleAndroid";
     public static final String SHOULD_IGNORE_INTENT_SKIP_INTERNAL_CHECK =
             "ShouldIgnoreIntentSkipInternalCheck";
@@ -455,7 +486,10 @@
     public static final String SEND_TAB_TO_SELF_SIGNIN_PROMO = "SendTabToSelfSigninPromo";
     public static final String SEND_TAB_TO_SELF_V2 = "SendTabToSelfV2";
     public static final String SHARED_HIGHLIGHTING_AMP = "SharedHighlightingAmp";
+    public static final String SHARE_CROW_BUTTON = "ShareCrowButton";
+    public static final String SHARE_CROW_BUTTON_LAUNCH_TAB = "ShareCrowLaunchTab";
     public static final String SHOPPING_LIST = "ShoppingList";
+    public static final String SHOW_EXTENDED_PRELOADING_SETTING = "ShowExtendedPreloadingSetting";
     public static final String SHOW_SCROLLABLE_MVT_ON_NTP_ANDROID = "ShowScrollableMVTOnNTPAndroid";
     public static final String SKIP_SERVICE_WORKER_FOR_INSTALL_PROMPT =
             "SkipServiceWorkerForInstallPromot";
@@ -507,13 +541,18 @@
             "TrustedWebActivityQualityEnforcementForced";
     public static final String TRUSTED_WEB_ACTIVITY_QUALITY_ENFORCEMENT_WARNING =
             "TrustedWebActivityQualityEnforcementWarning";
+    public static final String UNIFIED_CREDENTIAL_MANAGER_DRY_RUN =
+            "UnifiedCredentialManagerDryRun";
     public static final String UNIFIED_PASSWORD_MANAGER_ANDROID = "UnifiedPasswordManagerAndroid";
     public static final String UNIFIED_PASSWORD_MANAGER_ANDROID_BRANDING =
             "UnifiedPasswordManagerAndroidBranding";
     public static final String UNIFIED_PASSWORD_MANAGER_ERROR_MESSAGES =
             "UnifiedPasswordManagerErrorMessages";
     public static final String UPCOMING_SHARING_FEATURES = "UpcomingSharingFeatures";
+    public static final String UPDATE_NOTIFICATION_IMMEDIATE_SHOW_OPTION =
+            "UpdateNotificationScheduleServiceImmediateShowOption";
     public static final String USE_CHIME_ANDROID_SDK = "UseChimeAndroidSdk";
+    public static final String USE_CLIENT_CONFIG_IPH = "UseClientConfigIPH";
     public static final String USE_LIBUNWINDSTACK_NATIVE_UNWINDER_ANDROID =
             "UseLibunwindstackNativeUnwinderAndroid";
     public static final String VIDEO_TUTORIALS = "VideoTutorials";
@@ -521,6 +560,8 @@
     public static final String VOICE_SEARCH_AUDIO_CAPTURE_POLICY = "VoiceSearchAudioCapturePolicy";
     public static final String WEBNOTES_STYLIZE = "WebNotesStylize";
     public static final String WEB_APK_ALLOW_ICON_UPDATA = "WebApkAllowIconUpdate";
+    public static final String WEB_APK_INSTALL_COMPLETE_NOTIFICATION =
+            "WebApkInstallCompleteNotification";
     public static final String WEB_APK_INSTALL_SERVICE = "WebApkInstallService";
     public static final String WEB_APK_TRAMPOLINE_ON_INITIAL_INTENT =
             "WebApkTrampolineOnInitialIntent";
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClusterView.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClusterView.java
index 98ca850..086958b 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClusterView.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClusterView.java
@@ -81,7 +81,7 @@
     protected void onClick() {}
 
     @Override
-    protected @Nullable ColorStateList getDefaultStartIconTint() {
+    protected @Nullable ColorStateList getDefaultIconTint() {
         return ColorStateList.valueOf(
                 SemanticColorUtils.getDefaultIconColorSecondary(getContext()));
     }
@@ -94,13 +94,9 @@
         mDescriptionView.setText(text);
     }
 
-    void setIconDrawable(Drawable drawable) {
-        super.setStartIconDrawable(drawable);
-    }
-
     public void setStartIconBackgroundRes(@DrawableRes int resId) {
         mStartIconView.setBackgroundResource(resId);
-        ImageViewCompat.setImageTintList(mStartIconView, getDefaultStartIconTint());
+        ImageViewCompat.setImageTintList(mStartIconView, getDefaultIconTint());
     }
 
     void setEndButtonDrawable(Drawable drawable) {
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersItemView.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersItemView.java
index bb182274..7e5ea37 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersItemView.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersItemView.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.history_clusters;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.ImageView;
@@ -59,10 +58,6 @@
         mDescriptionView.setText(text);
     }
 
-    void setIconDrawable(Drawable drawable) {
-        super.setStartIconDrawable(drawable);
-    }
-
     void setEndButtonClickHandler(OnClickListener onClickListener) {
         mEndButtonView.setOnClickListener(onClickListener);
     }
diff --git a/chrome/browser/k_anonymity_service/k_anonymity_service_client.cc b/chrome/browser/k_anonymity_service/k_anonymity_service_client.cc
index ef987a9a..393b8dd4b 100644
--- a/chrome/browser/k_anonymity_service/k_anonymity_service_client.cc
+++ b/chrome/browser/k_anonymity_service/k_anonymity_service_client.cc
@@ -25,6 +25,7 @@
 #include "google_apis/google_api_keys.h"
 #include "net/base/isolation_info.h"
 #include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/data_decoder/public/cpp/data_decoder.h"
 #include "services/network/public/cpp/resource_request.h"
@@ -121,14 +122,28 @@
     }
   }
 
-  void OnCompleted(const absl::optional<std::string>& response,
-                   int net_error) override {
+  void OnCompleted(
+      network::mojom::ObliviousHttpCompletionResultPtr status) override {
     if (called_) {
       mojo::ReportBadMessage("OnCompleted called more than once");
       return;
     }
     called_ = true;
-    std::move(callback_).Run(response, net_error);
+    if (status->is_net_error()) {
+      std::move(callback_).Run(absl::nullopt, status->get_net_error());
+    } else if (status->is_outer_response_error_code()) {
+      std::move(callback_).Run(absl::nullopt,
+                               net::ERR_HTTP_RESPONSE_CODE_FAILURE);
+    } else {
+      DCHECK(status->is_inner_response());
+      if (status->get_inner_response()->response_code != net::HTTP_OK) {
+        std::move(callback_).Run(absl::nullopt,
+                                 net::ERR_HTTP_RESPONSE_CODE_FAILURE);
+      } else {
+        std::move(callback_).Run(status->get_inner_response()->response_body,
+                                 net::OK);
+      }
+    }
   }
 
  private:
diff --git a/chrome/browser/k_anonymity_service/k_anonymity_service_client_unittest.cc b/chrome/browser/k_anonymity_service/k_anonymity_service_client_unittest.cc
index 265f723a..96772d8 100644
--- a/chrome/browser/k_anonymity_service/k_anonymity_service_client_unittest.cc
+++ b/chrome/browser/k_anonymity_service/k_anonymity_service_client_unittest.cc
@@ -414,10 +414,29 @@
       return;
     }
     if (error_) {
-      remote_->OnCompleted(absl::nullopt, error_.value());
+      auto completion_result =
+          network::mojom::ObliviousHttpCompletionResult::NewNetError(
+              error_.value());
+      remote_->OnCompleted(std::move(completion_result));
       error_.reset();
+    } else if (outer_response_error_code_) {
+      auto completion_result = network::mojom::ObliviousHttpCompletionResult::
+          NewOuterResponseErrorCode(outer_response_error_code_.value());
+      remote_->OnCompleted(std::move(completion_result));
+      outer_response_error_code_.reset();
     } else {
-      remote_->OnCompleted(body, net::OK);
+      auto response = network::mojom::ObliviousHttpResponse::New();
+      if (inner_response_code_) {
+        response->response_code = inner_response_code_.value();
+        inner_response_code_.reset();
+      } else {
+        response->response_code = net::HTTP_OK;
+      }
+      response->response_body = std::move(body);
+      auto completion_result =
+          network::mojom::ObliviousHttpCompletionResult::NewInnerResponse(
+              std::move(response));
+      remote_->OnCompleted(std::move(completion_result));
     }
     remote_.reset();
     pending_request_.reset();
@@ -425,9 +444,17 @@
 
   void SetDropRequests(bool drop) { drop_requests_ = drop; }
   void SetErrorOnce(net::Error err) { error_ = err; }
+  void SetOuterResponseErrorOnce(int outer_response_error_code) {
+    outer_response_error_code_ = outer_response_error_code;
+  }
+  void SetInnerResponseErrorOnce(int inner_response_code) {
+    inner_response_code_ = inner_response_code;
+  }
 
  private:
   absl::optional<net::Error> error_;
+  absl::optional<int> outer_response_error_code_;
+  absl::optional<int> inner_response_code_;
   bool drop_requests_ = false;
   network::mojom::ObliviousHttpRequestPtr pending_request_;
   mojo::Remote<network::mojom::ObliviousHttpClient> remote_;
@@ -501,6 +528,14 @@
 
   void SetOhttpErrorOnce(net::Error err) { network_context_.SetErrorOnce(err); }
 
+  void SetOhttpOuterResponseErrorOnce(int outer_response_error_code) {
+    network_context_.SetOuterResponseErrorOnce(outer_response_error_code);
+  }
+
+  void SetOhttpInnerResponseErrorOnce(int inner_response_code) {
+    network_context_.SetInnerResponseErrorOnce(inner_response_code);
+  }
+
  private:
   OhttpTestNetworkContext network_context_;
   mojo::Receiver<network::mojom::NetworkContext> network_context_receiver_;
@@ -597,7 +632,8 @@
              {KAnonymityServiceJoinSetAction::kJoinSetSuccess, 1}});
 }
 
-TEST_F(KAnonymityServiceClientJoinQueryTest, TryJoinSetOtherErrorsNotRetried) {
+TEST_F(KAnonymityServiceClientJoinQueryTest,
+       TryJoinSetOtherNetErrorsNotRetried) {
   InitializeIdentity(true);
   SetOhttpErrorOnce(net::ERR_FAILED);
   KAnonymityServiceClient k_service(profile());
@@ -621,6 +657,54 @@
 }
 
 TEST_F(KAnonymityServiceClientJoinQueryTest,
+       TryJoinSetOtherOuterHttpStatusErrorNotRetried) {
+  InitializeIdentity(true);
+  SetOhttpOuterResponseErrorOnce(net::HTTP_NOT_FOUND);
+  KAnonymityServiceClient k_service(profile());
+  base::HistogramTester hist;
+  base::RunLoop run_loop;
+  k_service.JoinSet("1", base::BindLambdaForTesting([&run_loop](bool result) {
+                      EXPECT_FALSE(result);
+                      run_loop.Quit();
+                    }));
+  RespondWithJoinKey();
+  RespondWithTrustTokenNonUniqueUserID(2);
+  RespondWithTrustTokenKeys(2);
+  RespondWithTrustTokenIssued(2);
+  RespondWithJoin();
+  run_loop.Run();
+  CheckJoinSetHistogramActions(
+      hist, {{KAnonymityServiceJoinSetAction::kJoinSet, 1},
+             {KAnonymityServiceJoinSetAction::kFetchJoinSetOHTTPKey, 1},
+             {KAnonymityServiceJoinSetAction::kSendJoinSetRequest, 1},
+             {KAnonymityServiceJoinSetAction::kJoinSetRequestFailed, 1}});
+}
+
+TEST_F(KAnonymityServiceClientJoinQueryTest,
+       TryJoinSetOtherInnerHttpStatusErrorNotRetried) {
+  InitializeIdentity(true);
+  SetOhttpInnerResponseErrorOnce(net::HTTP_NOT_FOUND);
+  KAnonymityServiceClient k_service(profile());
+  base::HistogramTester hist;
+  base::RunLoop run_loop;
+  k_service.JoinSet("1", base::BindLambdaForTesting([&run_loop](bool result) {
+                      EXPECT_FALSE(result);
+                      run_loop.Quit();
+                    }));
+  RespondWithJoinKey();
+  RespondWithTrustTokenNonUniqueUserID(2);
+  RespondWithTrustTokenKeys(2);
+  RespondWithTrustTokenIssued(2);
+  RespondWithJoin();
+  run_loop.Run();
+  CheckJoinSetHistogramActions(
+      hist, {{KAnonymityServiceJoinSetAction::kJoinSet, 1},
+             {KAnonymityServiceJoinSetAction::kFetchJoinSetOHTTPKey, 1},
+             {KAnonymityServiceJoinSetAction::kSendJoinSetRequest, 1},
+             {KAnonymityServiceJoinSetAction::kJoinSetRequestFailed, 1}});
+}
+
+TEST_F(KAnonymityServiceClientJoinQueryTest,
        TryJoinSetTokenAlreadyRetriedTooMany) {
   InitializeIdentity(true);
   KAnonymityServiceClient k_service(profile());
diff --git a/chrome/browser/performance_manager/metrics/metrics_provider.cc b/chrome/browser/performance_manager/metrics/metrics_provider.cc
index 18a234d2e..52fa7342 100644
--- a/chrome/browser/performance_manager/metrics/metrics_provider.cc
+++ b/chrome/browser/performance_manager/metrics/metrics_provider.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/performance_manager/metrics/metrics_provider.h"
 
 #include "base/metrics/histogram_functions.h"
+#include "base/system/sys_info.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
 #include "components/performance_manager/public/user_tuning/prefs.h"
 #include "components/prefs/pref_service.h"
@@ -15,6 +17,7 @@
 
 MetricsProvider* g_metrics_provider = nullptr;
 
+uint64_t kBytesPerMb = 1024 * 1024;
 }
 
 // static
@@ -65,6 +68,11 @@
     : local_state_(local_state) {
   DCHECK(!g_metrics_provider);
   g_metrics_provider = this;
+
+  available_memory_metrics_timer_.Start(
+      FROM_HERE, base::Minutes(2),
+      base::BindRepeating(&MetricsProvider::RecordAvailableMemoryMetrics,
+                          base::Unretained(this)));
 }
 
 void MetricsProvider::OnBatterySaverModeChanged(bool is_active) {
@@ -114,4 +122,14 @@
   return EfficiencyMode::kNormal;
 }
 
+void MetricsProvider::RecordAvailableMemoryMetrics() {
+  auto available_bytes = base::SysInfo::AmountOfAvailablePhysicalMemory();
+  auto total_bytes = base::SysInfo::AmountOfPhysicalMemory();
+
+  base::UmaHistogramMemoryLargeMB("Memory.Experimental.AvailableMemoryMB",
+                                  available_bytes / kBytesPerMb);
+  base::UmaHistogramPercentage("Memory.Experimental.AvailableMemoryPercent",
+                               available_bytes * 100 / total_bytes);
+}
+
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/metrics/metrics_provider.h b/chrome/browser/performance_manager/metrics/metrics_provider.h
index 5026639b..1cd8a98 100644
--- a/chrome/browser/performance_manager/metrics/metrics_provider.h
+++ b/chrome/browser/performance_manager/metrics/metrics_provider.h
@@ -60,6 +60,8 @@
   void OnTuningModesChanged();
   EfficiencyMode ComputeCurrentMode() const;
 
+  void RecordAvailableMemoryMetrics();
+
   PrefChangeRegistrar pref_change_registrar_;
   const raw_ptr<PrefService> local_state_;
   EfficiencyMode current_mode_ = EfficiencyMode::kNormal;
@@ -67,6 +69,8 @@
   bool battery_saver_enabled_ = false;
 
   bool initialized_ = false;
+
+  base::RepeatingTimer available_memory_metrics_timer_;
 };
 
 }  // namespace performance_manager
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc
index 08e3ca7..73bd9bc 100644
--- a/chrome/browser/profiles/profile_manager_unittest.cc
+++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -74,7 +74,7 @@
 #include "chrome/browser/ash/login/users/scoped_test_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
-#include "chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
 #include "chrome/browser/ui/ash/test_wallpaper_controller.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client_impl.h"
 #include "components/user_manager/fake_user_manager.h"
@@ -164,7 +164,7 @@
     ash::UserImageManagerImpl::SkipDefaultUserImageDownloadForTesting();
     wallpaper_controller_client_ = std::make_unique<
         WallpaperControllerClientImpl>(
-        std::make_unique<wallpaper_handlers::TestBackdropFetcherDelegate>());
+        std::make_unique<wallpaper_handlers::TestWallpaperFetcherDelegate>());
     wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_);
 
     // Have to manually reset the session type in between test runs because
diff --git a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
index bba9f7c..6eca586 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
@@ -88,6 +88,9 @@
     if (child.role !== Role.STATIC_TEXT) {
       return false;
     }
+    if (child.name === undefined) {
+      return false;
+    }
     if (name.substring(nameIndex, nameIndex + child.name.length) !==
         child.name) {
       return false;
diff --git a/chrome/browser/resources/settings/about_page/about_page.html b/chrome/browser/resources/settings/about_page/about_page.html
index 13262ca..699bcdd 100644
--- a/chrome/browser/resources/settings/about_page/about_page.html
+++ b/chrome/browser/resources/settings/about_page/about_page.html
@@ -129,13 +129,13 @@
           hidden="[[!prefs.feedback_allowed.value]]"
           button-aria-description="$i18n{opensInNewTab}"
           label="$i18n{aboutReportAnIssue}" external></cr-link-row>
-</if>
-      <cr-link-row class="hr" id="getTheMostOutOfProgram"
-          on-click="onGetTheMostOutOfProgramTap_"
-          label="$i18n{getTheMostOutOfProgram}"
-          sub-label="$i18n{getTheMostOutOfProgramDescription}"
+      <cr-link-row class="hr" id="getTheMostOutOfChrome"
+          on-click="onGetTheMostOutOfChromeTap_"
+          label="$i18n{getTheMostOutOfChrome}"
+          sub-label="$i18n{getTheMostOutOfChromeDescription}"
           role-description="$i18n{subpageArrowRoleDescription}"
-          hidden$="[[!showGetTheMostOutOfProgramSection_]]"></cr-link-row>
+          hidden$="[[!showGetTheMostOutOfChromeSection_]]"></cr-link-row>
+</if>
       <cr-link-row class="hr" on-click="onManagementPageTap_"
           start-icon="cr:domain" label="$i18n{managementPage}"
           role-description="$i18n{subpageArrowRoleDescription}"
diff --git a/chrome/browser/resources/settings/about_page/about_page.ts b/chrome/browser/resources/settings/about_page/about_page.ts
index cf1a524..b0ede74 100644
--- a/chrome/browser/resources/settings/about_page/about_page.ts
+++ b/chrome/browser/resources/settings/about_page/about_page.ts
@@ -75,15 +75,17 @@
         },
       },
 
+      // <if expr="_google_chrome">
       /**
        * Whether to show the "Get the most out of Chrome" section.
        */
-      showGetTheMostOutOfProgramSection_: {
+      showGetTheMostOutOfChromeSection_: {
         type: Boolean,
         value() {
-          return loadTimeData.getBoolean('showGetTheMostOutOfProgramSection');
+          return loadTimeData.getBoolean('showGetTheMostOutOfChromeSection');
         },
       },
+      // </if>
 
       // <if expr="_google_chrome and is_macosx">
       promoteUpdaterStatus_: Object,
@@ -128,7 +130,10 @@
 
   private currentUpdateStatusEvent_: UpdateStatusChangedEvent|null;
   private isManaged_: boolean;
-  private showGetTheMostOutOfProgramSection_: boolean;
+
+  // <if expr="_google_chrome">
+  private showGetTheMostOutOfChromeSection_: boolean;
+  // </if>
 
   // <if expr="_google_chrome and is_macosx">
   private promoteUpdaterStatus_: PromoteUpdaterStatus;
@@ -213,10 +218,6 @@
     this.performRestart(RestartType.RELAUNCH);
   }
 
-  private onGetTheMostOutOfProgramTap_() {
-    // TODO(crbug.com/1423278): implement.
-  }
-
   // <if expr="not chromeos_ash">
   private updateShowUpdateStatus_() {
     if (this.obsoleteSystemInfo_.endOfLine) {
@@ -340,6 +341,10 @@
   private onReportIssueTap_() {
     this.aboutBrowserProxy_.openFeedbackDialog();
   }
+
+  private onGetTheMostOutOfChromeTap_() {
+    // TODO(crbug.com/1423278): implement.
+  }
   // </if>
 
   // <if expr="not chromeos_ash">
diff --git a/chrome/browser/resources/settings/autofill_page/autofill_page.ts b/chrome/browser/resources/settings/autofill_page/autofill_page.ts
index cfeb6dc6..407f8454 100644
--- a/chrome/browser/resources/settings/autofill_page/autofill_page.ts
+++ b/chrome/browser/resources/settings/autofill_page/autofill_page.ts
@@ -18,7 +18,6 @@
 import '../settings_shared.css.js';
 
 import {CrLinkRowElement} from 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
-import {OpenWindowProxyImpl} from 'chrome://resources/js/open_window_proxy.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {BaseMixin} from '../base_mixin.js';
@@ -29,7 +28,7 @@
 
 import {getTemplate} from './autofill_page.html.js';
 import {PasswordCheckMixin} from './password_check_mixin.js';
-import {PasswordManagerImpl} from './password_manager_proxy.js';
+import {PasswordManagerImpl, PasswordManagerPage} from './password_manager_proxy.js';
 import {PasswordRequestorMixin} from './password_requestor_mixin.js';
 import {PasswordViewPageInteractions, PasswordViewPageRequestedEvent, PasswordViewPageUrlParams, recordPasswordViewInteraction} from './password_view.js';
 
@@ -141,9 +140,8 @@
   private onPasswordsClick_() {
     PasswordManagerImpl.getInstance().recordPasswordsPageAccessInSettings();
     if (this.enableNewPasswordManagerPage_) {
-      // TODO(crbug.com/1416887): It will always open a new tab with Password
-      // Manager. Find a way to use chrome::ShowPasswordManager instead.
-      OpenWindowProxyImpl.getInstance().openUrl('chrome://password-manager');
+      PasswordManagerImpl.getInstance().showPasswordManager(
+          PasswordManagerPage.PASSWORDS);
       return;
     }
     Router.getInstance().navigateTo(routes.PASSWORDS);
diff --git a/chrome/browser/resources/settings/autofill_page/password_manager_proxy.ts b/chrome/browser/resources/settings/autofill_page/password_manager_proxy.ts
index 88def41..c63ab5f 100644
--- a/chrome/browser/resources/settings/autofill_page/password_manager_proxy.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_manager_proxy.ts
@@ -25,6 +25,13 @@
 
 export type PasswordManagerAuthTimeoutListener = () => void;
 
+// WARNING: Keep synced with
+// chrome/browser/ui/webui/settings/password_manager_handler.cc.
+export enum PasswordManagerPage {
+  PASSWORDS = 0,
+  CHECKUP = 1,
+}
+
 /**
  * Interface for all callbacks to the password API.
  */
@@ -316,6 +323,11 @@
    * successful authentication.
    */
   switchBiometricAuthBeforeFillingState(): void;
+
+  /**
+   * Shows new Password Manager UI (chrome://password-manager).
+   */
+  showPasswordManager(page: PasswordManagerPage): void;
 }
 
 /**
@@ -587,6 +599,10 @@
         PasswordCheckReferrer.COUNT);
   }
 
+  showPasswordManager(page: PasswordManagerPage) {
+    chrome.send('showPasswordManager', [page]);
+  }
+
   static getInstance(): PasswordManagerProxy {
     return instance || (instance = new PasswordManagerImpl());
   }
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_child.ts b/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_child.ts
index 8ea58b6..46ec08eb 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_child.ts
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_child.ts
@@ -10,10 +10,9 @@
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
 import {assertNotReached} from 'chrome://resources/js/assert_ts.js';
-import {OpenWindowProxyImpl} from 'chrome://resources/js/open_window_proxy.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {PasswordCheckReferrer, PasswordManagerImpl} from '../autofill_page/password_manager_proxy.js';
+import {PasswordCheckReferrer, PasswordManagerImpl, PasswordManagerPage} from '../autofill_page/password_manager_proxy.js';
 import {loadTimeData} from '../i18n_setup.js';
 import {MetricsBrowserProxy, MetricsBrowserProxyImpl, SafetyCheckInteractions} from '../metrics_browser_proxy.js';
 import {routes} from '../route.js';
@@ -162,10 +161,8 @@
     PasswordManagerImpl.getInstance().recordPasswordCheckReferrer(
         PasswordCheckReferrer.SAFETY_CHECK);
     if (this.enableNewPasswordManagerPage_) {
-      // TODO(crbug.com/1416887): It will always open a new tab with Password
-      // Manager. Find a way to use chrome::ShowPasswordCheck instead.
-      OpenWindowProxyImpl.getInstance().openUrl(
-          'chrome://password-manager/checkup');
+      PasswordManagerImpl.getInstance().showPasswordManager(
+          PasswordManagerPage.CHECKUP);
       return;
     }
     Router.getInstance().navigateTo(
diff --git a/chrome/browser/resources/settings/settings.ts b/chrome/browser/resources/settings/settings.ts
index 22cc8c7f..423d5f4 100644
--- a/chrome/browser/resources/settings/settings.ts
+++ b/chrome/browser/resources/settings/settings.ts
@@ -25,7 +25,7 @@
 export {SettingsAppearancePageElement, SystemTheme} from './appearance_page/appearance_page.js';
 export {HomeUrlInputElement} from './appearance_page/home_url_input.js';
 export {SettingsAutofillPageElement} from './autofill_page/autofill_page.js';
-export {AccountStorageOptInStateChangedListener, CredentialsChangedListener, PasswordCheckInteraction, PasswordCheckReferrer, PasswordCheckStatusChangedListener, PasswordExceptionListChangedListener, PasswordManagerAuthTimeoutListener, PasswordManagerImpl, PasswordManagerProxy, PasswordsFileExportProgressListener, SavedPasswordListChangedListener} from './autofill_page/password_manager_proxy.js';
+export {AccountStorageOptInStateChangedListener, CredentialsChangedListener, PasswordCheckInteraction, PasswordCheckReferrer, PasswordCheckStatusChangedListener, PasswordExceptionListChangedListener, PasswordManagerAuthTimeoutListener, PasswordManagerImpl, PasswordManagerPage, PasswordManagerProxy, PasswordsFileExportProgressListener, SavedPasswordListChangedListener} from './autofill_page/password_manager_proxy.js';
 export {BaseMixin} from './base_mixin.js';
 export {SettingsBasicPageElement} from './basic_page/basic_page.js';
 export {ControlledRadioButtonElement} from './controls/controlled_radio_button.js';
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
index 0b6065f6..75eae9e 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
@@ -249,6 +249,10 @@
   return content_analysis_request_.user_action_id();
 }
 
+const std::string& BinaryUploadService::Request::tab_title() const {
+  return content_analysis_request_.request_data().tab_title();
+}
+
 uint64_t BinaryUploadService::Request::user_action_requests_count() const {
   return content_analysis_request_.user_action_requests_count();
 }
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
index 6dd2703..e48ddd1d 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
@@ -180,6 +180,7 @@
     const std::string& digest() const;
     const std::string& content_type() const;
     const std::string& user_action_id() const;
+    const std::string& tab_title() const;
     uint64_t user_action_requests_count() const;
     GURL tab_url() const;
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.cc
index bce2cec..290e911 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_browsertest_base.cc
@@ -43,12 +43,13 @@
       const std::string& source,
       const std::string& destination,
       const std::string& user_action_id,
+      const std::string& tab_title,
       safe_browsing::DeepScanAccessPoint access_point,
       const std::vector<base::FilePath>& paths,
       enterprise_connectors::FilesRequestHandler::CompletionCallback callback) {
     return base::WrapUnique(new UnresponsiveFilesRequestHandler(
         upload_service, profile, analysis_settings, url, source, destination,
-        user_action_id, access_point, paths, std::move(callback)));
+        user_action_id, tab_title, access_point, paths, std::move(callback)));
   }
 
  private:
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 388396e..b35a81f 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1640,6 +1640,8 @@
       "webui/settings/metrics_reporting_handler.h",
       "webui/settings/on_startup_handler.cc",
       "webui/settings/on_startup_handler.h",
+      "webui/settings/password_manager_handler.cc",
+      "webui/settings/password_manager_handler.h",
       "webui/settings/people_handler.cc",
       "webui/settings/people_handler.h",
       "webui/settings/performance_handler.cc",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/ActionChipsProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/ActionChipsProcessor.java
index de080cc..08d382e 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/ActionChipsProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/ActionChipsProcessor.java
@@ -92,10 +92,9 @@
 
             modelList.add(new ListItem(ActionChipsProperties.ViewType.CHIP, chipModel));
 
-            if (chip.hasPedalId()) {
-                mLastVisiblePedals.add(chip.getPedalID());
-            } else if (chip.hasActionId()
-                    && chip.getActionID() == OmniboxActionType.HISTORY_CLUSTERS) {
+            if (chip.getActionId() == OmniboxActionType.PEDAL) {
+                mLastVisiblePedals.add(chip.getPedalId());
+            } else if (chip.getActionId() == OmniboxActionType.HISTORY_CLUSTERS) {
                 mJourneysActionShownPosition = position;
             }
         }
@@ -108,8 +107,7 @@
     }
 
     private void executeAction(@NonNull OmniboxPedal omniboxPedal, int position) {
-        if (omniboxPedal.hasActionId()
-                && omniboxPedal.getActionID() == OmniboxActionType.HISTORY_CLUSTERS) {
+        if (omniboxPedal.getActionId() == OmniboxActionType.HISTORY_CLUSTERS) {
             RecordHistogram.recordEnumeratedHistogram("Omnibox.SuggestionUsed.ResumeJourney",
                     position, SuggestionsMetrics.MAX_AUTOCOMPLETE_POSITION);
         }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarFeatures.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarFeatures.java
index 70f44b2..c3c3f70 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarFeatures.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarFeatures.java
@@ -22,6 +22,8 @@
             new MutableFlagWithSafeDefault(ChromeFeatureList.SUPPRESS_TOOLBAR_CAPTURES, false);
     private static final MutableFlagWithSafeDefault sRecordSuppressionMetrics =
             new MutableFlagWithSafeDefault(ChromeFeatureList.RECORD_SUPPRESSION_METRICS, true);
+    private static final MutableFlagWithSafeDefault sDelayTransitionsForAnimation =
+            new MutableFlagWithSafeDefault(ChromeFeatureList.DELAY_TRANSITIONS_FOR_ANIMATION, true);
 
     /** Private constructor to avoid instantiation. */
     private ToolbarFeatures() {}
@@ -43,6 +45,16 @@
     }
 
     /**
+     * Returns whether the layout system will delay transitions between start/done hiding/showing
+     * for Android view animations or not. When this is delayed, the toolbar code will try to
+     * always draw itself from Android views during these transitions, to avoid letting the captured
+     * bitmap leak through during transitions. With suppression enabled, the captured bitmap is less
+     * reliable during these transitions.
+     */
+    public static boolean shouldDelayTransitionsForAnimation() {
+        return sDelayTransitionsForAnimation.isEnabled();
+    }
+    /**
      * Returns whether to record metrics from suppression experiment. This allows an arm of
      * suppression to run without the overhead from reporting any extra metrics in Java. Using a
      * feature instead of a param to utilize Java side caching.
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index 471af534..749048e 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -32,6 +32,7 @@
 import org.chromium.base.lifetime.DestroyChecker;
 import org.chromium.base.lifetime.Destroyable;
 import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate;
+import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.omnibox.LocationBar;
 import org.chromium.chrome.browser.omnibox.LocationBarCoordinator;
 import org.chromium.chrome.browser.omnibox.NewTabPageDelegate;
@@ -877,4 +878,16 @@
             shadow.setVisibility(isHairlineVisible ? VISIBLE : GONE);
         }
     }
+
+    /**
+     * To be called indirectly by
+     * {@link LayoutStateProvider.LayoutStateObserver#onStartedHiding(int, boolean, boolean)}.
+     */
+    public void onTransitionStart() {}
+
+    /**
+     * To be called indirectly by
+     * {@link LayoutStateProvider.LayoutStateObserver#onFinishedShowing(int)}.
+     */
+    public void onTransitionEnd() {}
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 4b47198..0bd8142 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -271,6 +271,9 @@
 
     private boolean mDropdownListScrolled;
 
+    // If we're in a layout transition, between startHiding to doneShowing.
+    private boolean mInLayoutTransition;
+
     // The following are some properties used during animation.  We use explicit property classes
     // to avoid the cost of reflection for each animation setup.
 
@@ -798,6 +801,10 @@
                     return ChromeColors.getDefaultThemeColor(getContext(), false);
                 }
 
+                // During transition we cannot rely on the background to be opaque yet, so keep full
+                // alpha until the transition is over.
+                int alpha = mInLayoutTransition ? 255 : Math.round(mUrlExpansionFraction * 255);
+
                 // When the NTP fake search box is visible, the background color should be
                 // transparent. When the location bar reaches the top of the screen (i.e. location
                 // bar is fully expanded), the background needs to change back to the default
@@ -805,8 +812,7 @@
                 // between the transition, we set a translucent default toolbar color based on
                 // the expansion progress of the toolbar.
                 return androidx.core.graphics.ColorUtils.setAlphaComponent(
-                        ChromeColors.getDefaultThemeColor(getContext(), false),
-                        Math.round(mUrlExpansionFraction * 255));
+                        ChromeColors.getDefaultThemeColor(getContext(), false), alpha);
             case VisualState.NORMAL:
                 return ChromeColors.getDefaultThemeColor(getContext(), false);
             case VisualState.INCOGNITO:
@@ -1404,8 +1410,14 @@
      *         toolbar.
      */
     private boolean shouldDrawLocationBar() {
-        return mLocationBarBackground != null
-                && (mTabSwitcherState == STATIC_TAB || mTextureCaptureMode);
+        if (ToolbarFeatures.shouldDelayTransitionsForAnimation()) {
+            // The location bar should have alpha or clip+translation when its not supposed to be
+            // shown. Needs to be drawn during transitions, such as entering/exiting tab switcher.
+            return mLocationBarBackground != null;
+        } else {
+            return mLocationBarBackground != null
+                    && (mTabSwitcherState == STATIC_TAB || mTextureCaptureMode);
+        }
     }
 
     private boolean drawLocationBar(Canvas canvas, long drawingTime) {
@@ -1413,7 +1425,6 @@
         boolean clipped = false;
         if (shouldDrawLocationBar()) {
             canvas.save();
-
             if (shouldDrawLocationBarBackground()) {
                 if (mActiveLocationBarBackground instanceof NtpSearchBoxDrawable) {
                     ((NtpSearchBoxDrawable) mActiveLocationBarBackground)
@@ -1806,6 +1817,12 @@
             updateViewsForTabSwitcherMode();
         }
 
+        if (ToolbarFeatures.shouldDelayTransitionsForAnimation()) {
+            // Since mTabSwitcherState has changed, we need to also check if mVisualState should
+            // change.
+            updateVisualsForLocationBarState();
+        }
+
         updateButtonsTranslationY();
 
         if (DeviceClassManager.enableAccessibilityLayout(getContext())) {
@@ -2743,4 +2760,17 @@
     private int calculateOnFocusHeightIncrease() {
         return (int) (mBackgroundHeightIncreaseWhenFocus * mUrlFocusChangeFraction / 2);
     }
+
+    @Override
+    public void onTransitionStart() {
+        setVisibility(View.VISIBLE);
+        mInLayoutTransition = true;
+        updateToolbarBackgroundFromState(mVisualState);
+    }
+
+    @Override
+    public void onTransitionEnd() {
+        mInLayoutTransition = false;
+        updateToolbarBackgroundFromState(mVisualState);
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index 467f64e8..b07f98c58 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -806,4 +806,12 @@
     public boolean isBrowsingModeToolbarVisible() {
         return mToolbarLayout.getVisibility() == View.VISIBLE;
     }
+
+    public void onTransitionStart() {
+        mToolbarLayout.onTransitionStart();
+    }
+
+    public void onTransitionEnd() {
+        mToolbarLayout.onTransitionEnd();
+    }
 }
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 6950312..407020b 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -29,7 +29,7 @@
 #include "chrome/browser/ash/privacy_hub/privacy_hub_util.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/sync/sync_error_notifier_factory.h"
-#include "chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/tablet_mode/tablet_mode_page_behavior.h"
@@ -214,7 +214,7 @@
   // ash::ChromeUserManagerImpl.
   wallpaper_controller_client_ =
       std::make_unique<WallpaperControllerClientImpl>(
-          std::make_unique<wallpaper_handlers::BackdropFetcherDelegateImpl>());
+          std::make_unique<wallpaper_handlers::WallpaperFetcherDelegateImpl>());
   wallpaper_controller_client_->Init();
 
   session_controller_client_ = std::make_unique<SessionControllerClientImpl>();
diff --git a/chrome/browser/ui/ash/projector/projector_app_client_impl.cc b/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
index 3c5b06b..ec8185c 100644
--- a/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
+++ b/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
@@ -9,8 +9,8 @@
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/projector/annotator_tool.h"
-#include "ash/webui/projector_app/annotator_page_handler_impl.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
+#include "ash/webui/projector_app/untrusted_annotator_page_handler_impl.h"
 #include "base/functional/bind.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
@@ -156,12 +156,12 @@
 }
 
 void ProjectorAppClientImpl::SetAnnotatorPageHandler(
-    ash::AnnotatorPageHandlerImpl* handler) {
+    ash::UntrustedAnnotatorPageHandlerImpl* handler) {
   annotator_handler_ = handler;
 }
 
 void ProjectorAppClientImpl::ResetAnnotatorPageHandler(
-    ash::AnnotatorPageHandlerImpl* handler) {
+    ash::UntrustedAnnotatorPageHandlerImpl* handler) {
   if (annotator_handler_ == handler) {
     annotator_handler_ = nullptr;
   }
diff --git a/chrome/browser/ui/ash/projector/projector_app_client_impl.h b/chrome/browser/ui/ash/projector/projector_app_client_impl.h
index a25a270..cfba1d4a 100644
--- a/chrome/browser/ui/ash/projector/projector_app_client_impl.h
+++ b/chrome/browser/ui/ash/projector/projector_app_client_impl.h
@@ -8,8 +8,8 @@
 #include <memory>
 
 #include "ash/public/cpp/projector/projector_annotator_controller.h"
-#include "ash/webui/projector_app/annotator_page_handler_impl.h"
 #include "ash/webui/projector_app/projector_app_client.h"
+#include "ash/webui/projector_app/untrusted_annotator_page_handler_impl.h"
 #include "base/observer_list.h"
 #include "chrome/browser/ui/ash/projector/pending_screencast_manager.h"
 #include "chrome/browser/ui/ash/projector/screencast_manager.h"
@@ -52,9 +52,10 @@
       const std::string& video_file_id,
       const std::string& resource_key,
       ash::ProjectorAppClient::OnGetVideoCallback callback) const override;
-  void SetAnnotatorPageHandler(ash::AnnotatorPageHandlerImpl* handler) override;
+  void SetAnnotatorPageHandler(
+      ash::UntrustedAnnotatorPageHandlerImpl* handler) override;
   void ResetAnnotatorPageHandler(
-      ash::AnnotatorPageHandlerImpl* handler) override;
+      ash::UntrustedAnnotatorPageHandlerImpl* handler) override;
   void SetTool(const ash::AnnotatorTool& tool) override;
   void Clear() override;
   void NotifyAppUIActive(bool active) override;
@@ -62,7 +63,7 @@
       const std::vector<base::FilePath>& screencast_paths,
       bool suppress) override;
 
-  ash::AnnotatorPageHandlerImpl* get_annotator_handler_for_test() {
+  ash::UntrustedAnnotatorPageHandlerImpl* get_annotator_handler_for_test() {
     return annotator_handler_;
   }
   PendingScreencastManager* get_pending_screencast_manager_for_test() {
@@ -80,7 +81,7 @@
 
   ash::ScreencastManager screencast_manager_;
 
-  ash::AnnotatorPageHandlerImpl* annotator_handler_ = nullptr;
+  ash::UntrustedAnnotatorPageHandlerImpl* annotator_handler_ = nullptr;
 };
 
 #endif  // CHROME_BROWSER_UI_ASH_PROJECTOR_PROJECTOR_APP_CLIENT_IMPL_H_
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl.cc b/chrome/browser/ui/ash/projector/projector_client_impl.cc
index 7bd0e908..57c6184 100644
--- a/chrome/browser/ui/ash/projector/projector_client_impl.cc
+++ b/chrome/browser/ui/ash/projector/projector_client_impl.cc
@@ -103,7 +103,7 @@
 
 // static
 void ProjectorClientImpl::InitForProjectorAnnotator(views::WebView* web_view) {
-  web_view->LoadInitialURL(GURL(ash::kChromeUITrustedAnnotatorUrl));
+  web_view->LoadInitialURL(GURL(ash::kChromeUIUntrustedAnnotatorUrl));
 }
 
 // Using base::Unretained for callback is safe since the ProjectorClientImpl
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc b/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc
index dcfe2e4..9d90f10 100644
--- a/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc
+++ b/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc
@@ -181,7 +181,6 @@
 IN_PROC_BROWSER_TEST_F(ProjectorClientTest, AppUrlsValid) {
   VerifyUrlValid(kChromeUITrustedProjectorUrl);
   VerifyUrlValid(kChromeUIUntrustedProjectorUrl);
-  VerifyUrlValid(kChromeUITrustedAnnotatorUrl);
   VerifyUrlValid(kChromeUIUntrustedAnnotatorUrl);
 }
 
diff --git a/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc b/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc
index 9d2e396c..bb82bc3 100644
--- a/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc
+++ b/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc
@@ -390,21 +390,6 @@
   EXPECT_EQ(tab->GetVisibleURL(), untrusted_annotator_url);
 }
 
-// Verifies that chrome://projector-annotator is not accessible when the app is
-// disabled.
-IN_PROC_BROWSER_TEST_F(ProjectorNavigationThrottleDisabledTest,
-                       TrustedAnnotatorNavigationInvalidUrl) {
-  GURL trusted_annotator_url(kChromeUITrustedAnnotatorUrl);
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), trusted_annotator_url));
-  content::WebContents* tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  ASSERT_TRUE(tab);
-  EXPECT_EQ(tab->GetController().GetVisibleEntry()->GetPageType(),
-            content::PAGE_TYPE_ERROR);
-  EXPECT_EQ(tab->GetVisibleURL(), trusted_annotator_url);
-}
-
 class ProjectorNavigationThrottleLocaleTest
     : public ProjectorNavigationThrottleTest,
       public testing::WithParamInterface<std::string> {
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
index 282a9b8..3d605f57 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
@@ -43,7 +43,7 @@
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/device_local_account.h"
 #include "chrome/browser/ash/wallpaper/wallpaper_drivefs_delegate_impl.h"
-#include "chrome/browser/ash/wallpaper_handlers/backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h"
 #include "chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -180,9 +180,9 @@
 }  // namespace
 
 WallpaperControllerClientImpl::WallpaperControllerClientImpl(
-    std::unique_ptr<wallpaper_handlers::BackdropFetcherDelegate>
-        backdrop_fetcher_delegate)
-    : backdrop_fetcher_delegate_(std::move(backdrop_fetcher_delegate)) {
+    std::unique_ptr<wallpaper_handlers::WallpaperFetcherDelegate>
+        wallpaper_fetcher_delegate)
+    : wallpaper_fetcher_delegate_(std::move(wallpaper_fetcher_delegate)) {
   local_state_ = g_browser_process->local_state();
   show_user_names_on_signin_subscription_ =
       ash::CrosSettings::Get()->AddSettingsObserver(
@@ -556,7 +556,8 @@
     const std::string& collection_id,
     FetchImagesForCollectionCallback callback) {
   auto images_info_fetcher =
-      backdrop_fetcher_delegate_->CreateBackdropImageInfoFetcher(collection_id);
+      wallpaper_fetcher_delegate_->CreateBackdropImageInfoFetcher(
+          collection_id);
   auto* images_info_fetcher_ptr = images_info_fetcher.get();
   images_info_fetcher_ptr->Start(
       base::BindOnce(&WallpaperControllerClientImpl::OnFetchImagesForCollection,
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.h b/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
index 584fc4f6..99a163b 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
@@ -29,7 +29,7 @@
 }
 
 namespace wallpaper_handlers {
-class BackdropFetcherDelegate;
+class WallpaperFetcherDelegate;
 }  // namespace wallpaper_handlers
 
 // Handles chrome-side wallpaper control alongside the ash-side controller.
@@ -39,8 +39,8 @@
       public session_manager::SessionManagerObserver {
  public:
   explicit WallpaperControllerClientImpl(
-      std::unique_ptr<wallpaper_handlers::BackdropFetcherDelegate>
-          backdrop_fetcher_delegate);
+      std::unique_ptr<wallpaper_handlers::WallpaperFetcherDelegate>
+          wallpaper_fetcher_delegate);
 
   WallpaperControllerClientImpl(const WallpaperControllerClientImpl&) = delete;
   WallpaperControllerClientImpl& operator=(
@@ -213,8 +213,8 @@
            std::unique_ptr<wallpaper_handlers::GooglePhotosPhotosFetcher>>
       google_photos_photos_fetchers_;
 
-  const std::unique_ptr<wallpaper_handlers::BackdropFetcherDelegate>
-      backdrop_fetcher_delegate_;
+  const std::unique_ptr<wallpaper_handlers::WallpaperFetcherDelegate>
+      wallpaper_fetcher_delegate_;
 
   base::ScopedMultiSourceObservation<file_manager::VolumeManager,
                                      file_manager::VolumeManagerObserver>
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl_unittest.cc b/chrome/browser/ui/ash/wallpaper_controller_client_impl_unittest.cc
index 00b7d4c..b05e0b9 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl_unittest.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl_unittest.cc
@@ -10,7 +10,7 @@
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
-#include "chrome/browser/ash/wallpaper_handlers/test_backdrop_fetcher_delegate.h"
+#include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
 #include "chrome/browser/ui/ash/test_wallpaper_controller.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -61,7 +61,7 @@
   std::unique_ptr<user_manager::ScopedUserManager> user_manager_;
   TestWallpaperController controller_;
   WallpaperControllerClientImpl client_{
-      std::make_unique<wallpaper_handlers::TestBackdropFetcherDelegate>()};
+      std::make_unique<wallpaper_handlers::TestWallpaperFetcherDelegate>()};
 };
 
 TEST_F(WallpaperControllerClientImplTest, Construction) {
diff --git a/chrome/browser/ui/omnibox/omnibox_pedal_implementations.cc b/chrome/browser/ui/omnibox/omnibox_pedal_implementations.cc
index 04946bb..1793c98 100644
--- a/chrome/browser/ui/omnibox/omnibox_pedal_implementations.cc
+++ b/chrome/browser/ui/omnibox/omnibox_pedal_implementations.cc
@@ -88,7 +88,8 @@
   // This method override enables this Pedal to spoof its ID for metrics
   // reporting, making it possible to distinguish incognito usage.
   OmniboxPedalId GetMetricsId() const override {
-    return incognito_ ? OmniboxPedalId::INCOGNITO_CLEAR_BROWSING_DATA : id();
+    return incognito_ ? OmniboxPedalId::INCOGNITO_CLEAR_BROWSING_DATA
+                      : PedalId();
   }
 
  protected:
@@ -2035,7 +2036,9 @@
   std::unordered_map<OmniboxPedalId, scoped_refptr<OmniboxPedal>> pedals;
   const auto add = [&](OmniboxPedal* pedal) {
     const bool inserted =
-        pedals.insert(std::make_pair(pedal->id(), base::WrapRefCounted(pedal)))
+        pedals
+            .insert(
+                std::make_pair(pedal->PedalId(), base::WrapRefCounted(pedal)))
             .second;
     DCHECK(inserted);
   };
diff --git a/chrome/browser/ui/omnibox/omnibox_pedal_implementations_unittest.cc b/chrome/browser/ui/omnibox/omnibox_pedal_implementations_unittest.cc
index 399b59d..2f16eb6915 100644
--- a/chrome/browser/ui/omnibox/omnibox_pedal_implementations_unittest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_pedal_implementations_unittest.cc
@@ -17948,13 +17948,13 @@
         EXPECT_NE(iter, pedals.end()) << "Pedal not found for: " << expression;
         EXPECT_EQ(iter->second.get(), canonical_pedal)
             << "Found wrong Pedal for: " << expression;
-        const int found_id = static_cast<int>(iter->second->id());
+        const int found_id = static_cast<int>(iter->second->PedalId());
         std::advance(iter, 1);
         iter = std::find_if(iter, pedals.end(), is_match);
         EXPECT_EQ(iter, pedals.end())
             << "Found more than one Pedal match for: " << expression
             << " -- IDs: first " << found_id << " then "
-            << static_cast<int>(iter->second->id());
+            << static_cast<int>(iter->second->PedalId());
       }
     }
   }
@@ -17968,7 +17968,7 @@
 
 TEST_F(OmniboxPedalImplementationsTest, PedalClearBrowsingDataExecutes) {
   const OmniboxPedal* pedal = provider()->FindPedalMatch(u"clear browser data");
-  EXPECT_EQ(OmniboxPedalId::CLEAR_BROWSING_DATA, pedal->id());
+  EXPECT_EQ(OmniboxPedalId::CLEAR_BROWSING_DATA, pedal->PedalId());
 
   EXPECT_EQ(GURL("chrome://settings/clearBrowserData"),
             ExecuteContextAndReturnResult(pedal));
@@ -17981,7 +17981,7 @@
   // Note, there is only one Pedal for clearing browser data but it behaves
   // differently depending on incognito status. The incognito behavior does
   // not navigate but the non-incognito behavior does navigate.
-  EXPECT_EQ(OmniboxPedalId::CLEAR_BROWSING_DATA, pedal->id());
+  EXPECT_EQ(OmniboxPedalId::CLEAR_BROWSING_DATA, pedal->PedalId());
   EXPECT_EQ(GURL(""), ExecuteContextAndReturnResult(pedal));
 }
 
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 6673f990..75c0c67 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -72,10 +72,10 @@
              "EvDetailsInPageInfo",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+#if !defined(ANDROID) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
 // Enables showing the "Get the most out of Chrome" section in settings.
-#if !defined(ANDROID)
-BASE_FEATURE(kGetTheMostOutOfProgram,
-             "GetTheMostOutOfProgram",
+BASE_FEATURE(kGetTheMostOutOfChrome,
+             "GetTheMostOutOfChrome",
              base::FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index 19b2a553..3300b89 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -49,8 +49,8 @@
 
 BASE_DECLARE_FEATURE(kEvDetailsInPageInfo);
 
-#if !defined(ANDROID)
-BASE_DECLARE_FEATURE(kGetTheMostOutOfProgram);
+#if !defined(ANDROID) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+BASE_DECLARE_FEATURE(kGetTheMostOutOfChrome);
 #endif
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
index d9d28bea..1f5135e 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
@@ -570,7 +570,7 @@
   }
 
   auto bubble_delegate = std::make_unique<views::BubbleDialogDelegate>(
-      this, views::BubbleBorder::TOP_RIGHT);
+      overflow_button_, views::BubbleBorder::TOP_LEFT);
 
   bubble_delegate_ = bubble_delegate.get();
   bubble_delegate_->SetShowTitle(false);
@@ -578,9 +578,12 @@
   bubble_delegate_->SetButtons(ui::DIALOG_BUTTON_NONE);
   bubble_delegate_->set_margins(gfx::Insets());
   bubble_delegate_->set_fixed_width(200);
+  bubble_delegate_->set_adjust_if_offscreen(true);
+  bubble_delegate_->set_close_on_deactivate(true);
 
   overflow_menu_ =
       bubble_delegate_->SetContentsView(std::make_unique<OverflowMenu>(*this));
+
   const gfx::Insets insets = gfx::Insets::TLBR(16, 16, 16, 48);
   auto box = std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical, insets,
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
index 68bad348..a67903c 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
@@ -84,8 +84,12 @@
               &SavedTabGroupButton::CreateDialogModelForContextMenu,
               base::Unretained(this)),
           views::MenuRunner::CONTEXT_MENU | views::MenuRunner::IS_NESTED) {
+  SetAccessibilityProperties(
+      ax::mojom::Role::kPopUpButton, group.title(),
+      /*description*/ absl::nullopt,
+      l10n_util::GetStringUTF16(
+          IDS_ACCNAME_SAVED_TAB_GROUP_BUTTON_ROLE_DESCRIPTION));
   SetText(group.title());
-  SetAccessibleName(group.title());
   SetTooltipText(group.title());
   SetID(VIEW_ID_BOOKMARK_BAR_ELEMENT);
   SetProperty(views::kElementIdentifierKey, kSavedTabGroupButtonElementId);
@@ -157,17 +161,15 @@
 }
 
 void SavedTabGroupButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  views::MenuButton::GetAccessibleNodeData(node_data);
+
+  // TODO(crbug.com/1411342): Under what circumstances would there be no
+  // name? Please read the bug description and update accordingly.
   // If the button would have no name, avoid crashing by setting the name
   // explicitly empty.
   if (GetAccessibleName().empty()) {
     node_data->SetNameExplicitlyEmpty();
   }
-
-  views::MenuButton::GetAccessibleNodeData(node_data);
-  node_data->AddStringAttribute(
-      ax::mojom::StringAttribute::kRoleDescription,
-      l10n_util::GetStringUTF8(
-          IDS_ACCNAME_SAVED_TAB_GROUP_BUTTON_ROLE_DESCRIPTION));
 }
 
 void SavedTabGroupButton::OnPaintBackground(gfx::Canvas* canvas) {
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_item_unittest.cc b/chrome/browser/ui/views/extensions/extensions_menu_item_unittest.cc
index 33ca236b..54753d0fe 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_item_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_item_unittest.cc
@@ -70,8 +70,7 @@
   controller_->SetActionName(initial_extension_name_);
   controller_->SetTooltip(initial_tooltip_);
   auto menu_item = std::make_unique<ExtensionMenuItemView>(
-      browser(), std::move(controller), true,
-      /*is_site_permissions_button_visible=*/false);
+      browser(), std::move(controller), true);
   primary_button_ = menu_item->primary_action_button_for_testing();
   pin_button_ = menu_item->pin_button_for_testing();
   context_menu_button_ = menu_item->context_menu_button_for_testing();
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
index 5a13462..99c64538 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
@@ -65,91 +65,48 @@
 }
 
 }  // namespace
-
 ExtensionMenuItemView::ExtensionMenuItemView(
     Browser* browser,
     std::unique_ptr<ToolbarActionViewController> controller,
-    bool allow_pinning,
-    bool is_site_permissions_button_visible,
-    views::Button::PressedCallback site_permissions_button_callback)
+    bool allow_pinning)
     : browser_(browser),
       controller_(std::move(controller)),
       model_(ToolbarActionsModel::Get(browser_->profile())) {
+  CHECK(!base::FeatureList::IsEnabled(
+      extensions_features::kExtensionsMenuAccessControl));
+
   views::FlexSpecification stretch_specification =
       views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
                                views::MaximumFlexSizeRule::kUnbounded);
-
-  views::View* main_row;
   auto builder =
       views::Builder<ExtensionMenuItemView>(this)
           // Set so the extension button receives enter/exit on children to
           // retain hover status when hovering child views.
-          .SetNotifyEnterExitOnChild(true);
-
-  if (base::FeatureList::IsEnabled(
-          extensions_features::kExtensionsMenuAccessControl)) {
-    DCHECK(site_permissions_button_callback);
-
-    ChromeLayoutProvider* const provider = ChromeLayoutProvider::Get();
-    const int icon_size = provider->GetDistanceMetric(
-        DISTANCE_EXTENSIONS_MENU_EXTENSION_ICON_SIZE);
-    const int horizontal_inset =
-        provider->GetDistanceMetric(DISTANCE_EXTENSIONS_MENU_BUTTON_MARGIN);
-    const int icon_label_spacing =
-        provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL);
-
-    builder.SetOrientation(views::LayoutOrientation::kVertical)
-        .SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
-        .SetProperty(views::kFlexBehaviorKey, stretch_specification)
-        .AddChildren(
-            // Main row.
-            views::Builder<views::FlexLayoutView>()
-                .CopyAddressTo(&main_row)
-                .SetOrientation(views::LayoutOrientation::kHorizontal)
-                .SetIgnoreDefaultMainAxisMargins(true),
-            // Secondary row.
-            views::Builder<views::FlexLayoutView>().AddChildren(
-                // Site permissions button.
-                // TODO(crbug.com/998298): Compute title based on the extension
-                // site access.
-                // TODO(crbug.com/998298): Add tooltip after UX provides it.
-                views::Builder<HoverButton>(
-                    std::make_unique<HoverButton>(
-                        site_permissions_button_callback,
-                        /*icon_view=*/nullptr, u"site access", std::u16string(),
-                        std::make_unique<views::ImageView>(
-                            ui::ImageModel::FromVectorIcon(
-                                vector_icons::kSubmenuArrowIcon,
-                                ui::kColorIcon))))
-                    .CopyAddressTo(&site_permissions_button_)
-                    .SetVisible(is_site_permissions_button_visible)
-                    // Margin to align the main and secondary row text. Icon
-                    // size and horizontal insets should be the values used by
-                    // the extensions menu button.
-                    .SetProperty(
-                        views::kMarginsKey,
-                        gfx::Insets::VH(0, icon_size + horizontal_inset))
-                    // Border should be the same as the icon label
-                    // spacing used by the extensions menu button.
-                    .SetBorder(views::CreateEmptyBorder(
-                        gfx::Insets::VH(0, icon_label_spacing)))));
-  } else {
-    builder.CopyAddressTo(&main_row)
-        .SetOrientation(views::LayoutOrientation::kHorizontal)
-        .SetIgnoreDefaultMainAxisMargins(true);
-  }
-
-  std::move(builder).BuildChildren();
-
-  main_row->AddChildView(
-      views::Builder<ExtensionsMenuButton>(
-          std::make_unique<ExtensionsMenuButton>(browser_, controller_.get()))
-          .CopyAddressTo(&primary_action_button_)
-          .SetProperty(views::kFlexBehaviorKey, stretch_specification)
-          .Build());
+          .SetNotifyEnterExitOnChild(true)
+          .SetOrientation(views::LayoutOrientation::kHorizontal)
+          .SetIgnoreDefaultMainAxisMargins(true)
+          .AddChildren(
+              views::Builder<ExtensionsMenuButton>(
+                  std::make_unique<ExtensionsMenuButton>(browser_,
+                                                         controller_.get()))
+                  .CopyAddressTo(&primary_action_button_)
+                  .SetProperty(views::kFlexBehaviorKey, stretch_specification),
+              views::Builder<HoverButton>(
+                  std::make_unique<HoverButton>(
+                      views::Button::PressedCallback(), std::u16string()))
+                  .CopyAddressTo(&context_menu_button_)
+                  .SetID(EXTENSION_CONTEXT_MENU)
+                  .SetBorder(views::CreateEmptyBorder(
+                      ChromeLayoutProvider::Get()->GetDistanceMetric(
+                          DISTANCE_EXTENSIONS_MENU_BUTTON_MARGIN)))
+                  .SetTooltipText(l10n_util::GetStringUTF16(
+                      IDS_EXTENSIONS_MENU_CONTEXT_MENU_TOOLTIP)));
 
   if (allow_pinning) {
-    main_row->AddChildView(
+    // Pin button should be in between `primary_action_button_` and
+    // `context_menu_button_`.
+    int index = 1;
+    builder.AddChildAt(
         views::Builder<HoverButton>(
             std::make_unique<HoverButton>(
                 base::BindRepeating(&ExtensionMenuItemView::OnPinButtonPressed,
@@ -159,35 +116,97 @@
             .SetID(EXTENSION_PINNING)
             .SetBorder(views::CreateEmptyBorder(
                 ChromeLayoutProvider::Get()->GetDistanceMetric(
-                    DISTANCE_EXTENSIONS_MENU_BUTTON_MARGIN)))
-            .Build());
+                    DISTANCE_EXTENSIONS_MENU_BUTTON_MARGIN))),
+        index);
   }
 
-  main_row->AddChildView(
-      views::Builder<HoverButton>(
-          std::make_unique<HoverButton>(views::Button::PressedCallback(),
-                                        std::u16string()))
-          .CopyAddressTo(&context_menu_button_)
-          .SetID(EXTENSION_CONTEXT_MENU)
-          .SetBorder(views::CreateEmptyBorder(
-              ChromeLayoutProvider::Get()->GetDistanceMetric(
-                  DISTANCE_EXTENSIONS_MENU_BUTTON_MARGIN)))
-          .SetTooltipText(l10n_util::GetStringUTF16(
-              IDS_EXTENSIONS_MENU_CONTEXT_MENU_TOOLTIP))
-          .Build());
+  std::move(builder).BuildChildren();
 
-  // Add a controller to the context menu
-  context_menu_controller_ = std::make_unique<ExtensionContextMenuController>(
-      controller_.get(),
-      extensions::ExtensionContextMenuModel::ContextMenuSource::kMenuItem);
+  SetupContextMenuButton();
+}
 
-  context_menu_button_->SetButtonController(
-      std::make_unique<views::MenuButtonController>(
-          context_menu_button_.get(),
-          base::BindRepeating(&ExtensionMenuItemView::OnContextMenuPressed,
-                              base::Unretained(this)),
-          std::make_unique<views::Button::DefaultButtonControllerDelegate>(
-              context_menu_button_.get())));
+ExtensionMenuItemView::ExtensionMenuItemView(
+    Browser* browser,
+    std::unique_ptr<ToolbarActionViewController> controller,
+    SitePermissionsButtonState site_permissions_button_state,
+    views::Button::PressedCallback site_permissions_button_callback)
+    : browser_(browser),
+      controller_(std::move(controller)),
+      model_(ToolbarActionsModel::Get(browser_->profile())) {
+  CHECK(base::FeatureList::IsEnabled(
+      extensions_features::kExtensionsMenuAccessControl));
+
+  views::FlexSpecification stretch_specification =
+      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
+                               views::MaximumFlexSizeRule::kUnbounded);
+  ChromeLayoutProvider* const provider = ChromeLayoutProvider::Get();
+  const int icon_size =
+      provider->GetDistanceMetric(DISTANCE_EXTENSIONS_MENU_EXTENSION_ICON_SIZE);
+  const int horizontal_inset =
+      provider->GetDistanceMetric(DISTANCE_EXTENSIONS_MENU_BUTTON_MARGIN);
+  const int icon_label_spacing =
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL);
+
+  views::Builder<ExtensionMenuItemView>(this)
+      // Set so the extension button receives enter/exit on children to
+      // retain hover status when hovering child views.
+      .SetNotifyEnterExitOnChild(true)
+      .SetOrientation(views::LayoutOrientation::kVertical)
+      .SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
+      .SetProperty(views::kFlexBehaviorKey, stretch_specification)
+      .AddChildren(
+          // Main row.
+          views::Builder<views::FlexLayoutView>()
+              .SetOrientation(views::LayoutOrientation::kHorizontal)
+              .SetIgnoreDefaultMainAxisMargins(true)
+              .AddChildren(
+                  views::Builder<ExtensionsMenuButton>(
+                      std::make_unique<ExtensionsMenuButton>(browser_,
+                                                             controller_.get()))
+                      .CopyAddressTo(&primary_action_button_)
+                      .SetProperty(views::kFlexBehaviorKey,
+                                   stretch_specification),
+                  views::Builder<HoverButton>(
+                      std::make_unique<HoverButton>(
+                          views::Button::PressedCallback(), std::u16string()))
+                      .CopyAddressTo(&context_menu_button_)
+                      .SetID(EXTENSION_CONTEXT_MENU)
+                      .SetBorder(views::CreateEmptyBorder(
+                          ChromeLayoutProvider::Get()->GetDistanceMetric(
+                              DISTANCE_EXTENSIONS_MENU_BUTTON_MARGIN)))
+                      .SetTooltipText(l10n_util::GetStringUTF16(
+                          IDS_EXTENSIONS_MENU_CONTEXT_MENU_TOOLTIP))),
+          // Secondary row.
+          views::Builder<views::FlexLayoutView>().AddChildren(
+              // Site permissions button.
+              // TODO(crbug.com/998298): Compute title based on the
+              // extension site access.
+              // TODO(crbug.com/998298): Add tooltip after UX provides it.
+              views::Builder<HoverButton>(
+                  std::make_unique<HoverButton>(
+                      site_permissions_button_callback,
+                      /*icon_view=*/nullptr, u"site access", std::u16string(),
+                      std::make_unique<views::ImageView>(
+                          ui::ImageModel::FromVectorIcon(
+                              vector_icons::kSubmenuArrowIcon,
+                              ui::kColorIcon))))
+                  .CopyAddressTo(&site_permissions_button_)
+                  .SetVisible(site_permissions_button_state !=
+                              SitePermissionsButtonState::kHidden)
+                  .SetEnabled(site_permissions_button_state ==
+                              SitePermissionsButtonState::kEnabled)
+                  // Margin to align the main and secondary row text. Icon
+                  // size and horizontal insets should be the values used by
+                  // the extensions menu button.
+                  .SetProperty(views::kMarginsKey,
+                               gfx::Insets::VH(0, icon_size + horizontal_inset))
+                  // Border should be the same as the icon label
+                  // spacing used by the extensions menu button.
+                  .SetBorder(views::CreateEmptyBorder(
+                      gfx::Insets::VH(0, icon_label_spacing)))))
+      .BuildChildren();
+
+  SetupContextMenuButton();
 }
 
 ExtensionMenuItemView::~ExtensionMenuItemView() = default;
@@ -207,9 +226,15 @@
   UpdatePinButton();
 }
 
-void ExtensionMenuItemView::Update(bool is_site_permissions_button_visible) {
+void ExtensionMenuItemView::Update(
+    SitePermissionsButtonState site_permissions_button_state) {
   if (site_permissions_button_) {
-    site_permissions_button_->SetVisible(is_site_permissions_button_visible);
+    site_permissions_button_->SetVisible(site_permissions_button_state !=
+                                         SitePermissionsButtonState::kHidden);
+    site_permissions_button_->SetEnabled(site_permissions_button_state ==
+                                         SitePermissionsButtonState::kEnabled);
+    // TODO(crbug.com/1390952): Display the arrow icon only when site
+    // permissions button is enabled.
   }
 
   view_controller()->UpdateState();
@@ -247,6 +272,21 @@
                          icon_color, disabled_icon_color);
 }
 
+void ExtensionMenuItemView::SetupContextMenuButton() {
+  // Add a controller to the context menu
+  context_menu_controller_ = std::make_unique<ExtensionContextMenuController>(
+      controller_.get(),
+      extensions::ExtensionContextMenuModel::ContextMenuSource::kMenuItem);
+
+  context_menu_button_->SetButtonController(
+      std::make_unique<views::MenuButtonController>(
+          context_menu_button_.get(),
+          base::BindRepeating(&ExtensionMenuItemView::OnContextMenuPressed,
+                              base::Unretained(this)),
+          std::make_unique<views::Button::DefaultButtonControllerDelegate>(
+              context_menu_button_.get())));
+}
+
 bool ExtensionMenuItemView::IsPinned() const {
   // |model_| can be null in unit tests.
   return model_ && model_->IsActionPinned(controller_->GetId());
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_item_view.h b/chrome/browser/ui/views/extensions/extensions_menu_item_view.h
index 36a5afdd..a609c34 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_item_view.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_item_view.h
@@ -28,11 +28,24 @@
  public:
   METADATA_HEADER(ExtensionMenuItemView);
 
+  enum class SitePermissionsButtonState {
+    // Button is not visible.
+    kHidden,
+    // Button is visible, but disabled.
+    kDisabled,
+    // Button is visible and enabled.
+    kEnabled,
+  };
+
+  ExtensionMenuItemView(Browser* browser,
+                        std::unique_ptr<ToolbarActionViewController> controller,
+                        bool allow_pinning);
+
+  // Constructor for the kExtensionsMenuAccessControl feature.
   ExtensionMenuItemView(
       Browser* browser,
       std::unique_ptr<ToolbarActionViewController> controller,
-      bool allow_pinning,
-      bool is_site_permissions_button_visible,
+      SitePermissionsButtonState site_permissions_button_state,
       views::Button::PressedCallback site_permissions_button_callback =
           base::RepeatingClosure(base::NullCallback()));
   ExtensionMenuItemView(const ExtensionMenuItemView&) = delete;
@@ -43,7 +56,7 @@
   void OnThemeChanged() override;
 
   // Updates the controller and child views to be on sync with the parent views.
-  void Update(bool is_site_permissions_button_visible);
+  void Update(SitePermissionsButtonState site_permissions_button_state);
 
   // Updates the pin button.
   void UpdatePinButton();
@@ -66,6 +79,10 @@
   }
 
  private:
+  // Sets ups the context menu button controllers. Must be called by the
+  // constructor.
+  void SetupContextMenuButton();
+
   // Returns whether the action corresponding to this view is pinned to the
   // toolbar.
   bool IsPinned() const;
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
index 1a6c40f..9d19bff 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
@@ -224,12 +224,11 @@
 void ExtensionsMenuMainPageView::CreateAndInsertMenuItem(
     std::unique_ptr<ExtensionActionViewController> action_controller,
     extensions::ExtensionId extension_id,
-    bool allow_pinning,
-    bool is_site_permissions_button_visible,
+    ExtensionMenuItemView::SitePermissionsButtonState
+        site_permissions_button_state,
     int index) {
   auto item = std::make_unique<ExtensionMenuItemView>(
-      browser_, std::move(action_controller), allow_pinning,
-      is_site_permissions_button_visible,
+      browser_, std::move(action_controller), site_permissions_button_state,
       base::BindRepeating(
           &ExtensionsMenuNavigationHandler::OpenSitePermissionsPage,
           base::Unretained(navigation_handler_), extension_id));
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.h b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.h
index 135307c..b7bced8 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.h
@@ -7,6 +7,7 @@
 
 #include "base/memory/raw_ptr_exclusion.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
+#include "chrome/browser/ui/views/extensions/extensions_menu_item_view.h"
 #include "ui/views/view.h"
 
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -44,8 +45,8 @@
   void CreateAndInsertMenuItem(
       std::unique_ptr<ExtensionActionViewController> action_controller,
       extensions::ExtensionId extension_id,
-      bool allow_pinning,
-      bool is_site_permissions_button_visible,
+      ExtensionMenuItemView::SitePermissionsButtonState
+          site_permissions_button_state,
       int index);
 
   // Removes the menu item corresponding to `action_id`.
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view_unittest.cc
index 1c594531..de48d8a 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view_unittest.cc
@@ -29,29 +29,6 @@
 
 using PermissionsManager = extensions::PermissionsManager;
 
-// A scoper that manages a Browser instance created by BrowserWithTestWindowTest
-// beyond the default instance it creates in SetUp.
-class AdditionalBrowser {
- public:
-  explicit AdditionalBrowser(std::unique_ptr<Browser> browser)
-      : browser_(std::move(browser)),
-        browser_view_(BrowserView::GetBrowserViewForBrowser(browser_.get())) {}
-
-  ~AdditionalBrowser() {
-    // Tear down `browser_`, similar to TestWithBrowserView::TearDown.
-    browser_.release();
-    browser_view_->GetWidget()->CloseNow();
-  }
-
-  ExtensionsToolbarContainer* extensions_container() {
-    return browser_view_->toolbar()->extensions_container();
-  }
-
- private:
-  std::unique_ptr<Browser> browser_;
-  raw_ptr<BrowserView> browser_view_;
-};
-
 // Returns the extension names from the given `menu_items`.
 std::vector<std::string> GetNamesFromMenuItems(
     std::vector<ExtensionMenuItemView*> menu_items) {
@@ -86,7 +63,6 @@
   // nudge to re-layout the views.
   void LayoutMenuIfNecessary();
 
-  void ClickPinButton(ExtensionMenuItemView* menu_item);
   void ClickSitePermissionsButton(ExtensionMenuItemView* menu_item);
 
   content::WebContentsTester* web_contents_tester() {
@@ -127,12 +103,6 @@
   menu_coordinator()->GetExtensionsMenuWidget()->LayoutRootViewIfNecessary();
 }
 
-void ExtensionsMenuMainPageViewUnitTest::ClickPinButton(
-    ExtensionMenuItemView* menu_item) {
-  ClickButton(menu_item->pin_button_for_testing());
-  WaitForAnimation();
-}
-
 void ExtensionsMenuMainPageViewUnitTest::ClickSitePermissionsButton(
     ExtensionMenuItemView* menu_item) {
   ClickButton(menu_item->site_permissions_button_for_testing());
@@ -188,144 +158,141 @@
   EXPECT_EQ(GetNamesFromMenuItems(items), expected_items);
 }
 
-TEST_F(ExtensionsMenuMainPageViewUnitTest, PinnedExtensionAppearsInToolbar) {
-  constexpr char kName[] = "Extension";
-  const std::string& extension_id = InstallExtension(kName)->id();
-
-  ShowMenu();
-
-  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
-  ASSERT_TRUE(menu_item);
-  EXPECT_FALSE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
-
-  // Pin.
-  ClickPinButton(menu_item);
-  EXPECT_TRUE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
-  EXPECT_EQ(GetPinnedExtensionNames(), std::vector<std::string>{kName});
-
-  // Unpin.
-  ClickPinButton(menu_item);
-  EXPECT_FALSE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
-}
-
+// Verifies that the permissions button for all extension menu items is always
+// visible when user can customize site access by extension. However, enterprise
+// extensions and extensions that don't request host permissions should have the
+// button disabled as user cannot change their site access.
 TEST_F(ExtensionsMenuMainPageViewUnitTest,
-       NewPinnedExtensionAppearsToTheRightOfPinnedExtensions) {
-  constexpr char kExtensionA[] = "A Extension";
-  InstallExtension(kExtensionA);
-  constexpr char kExtensionB[] = "B Extension";
-  InstallExtension(kExtensionB);
-  constexpr char kExtensionC[] = "C Extension";
-  InstallExtension(kExtensionC);
-
-  ShowMenu();
-
-  std::vector<ExtensionMenuItemView*> items = menu_items();
-
-  // Verify the order of the extensions is A,B,C.
-  {
-    EXPECT_EQ(items.size(), 3u);
-    std::vector<std::string> expected_items{kExtensionA, kExtensionB,
-                                            kExtensionC};
-    EXPECT_EQ(GetNamesFromMenuItems(items), expected_items);
-  }
-
-  // Pinning an extension should add it to the toolbar.
-  {
-    ClickPinButton(items.at(0));
-    std::vector<std::string> expected_names{kExtensionA};
-    EXPECT_EQ(GetPinnedExtensionNames(), expected_names);
-  }
-
-  // Pinning a second extension should add it to the right of the current pinned
-  // extensions.
-  {
-    ClickPinButton(items.at(1));
-    std::vector<std::string> expected_names{kExtensionA, kExtensionB};
-    EXPECT_EQ(GetPinnedExtensionNames(), expected_names);
-  }
-
-  // Pinning a third extension should add it to the right of the current pinned
-  // extensions.
-  {
-    ClickPinButton(items.at(2));
-    std::vector<std::string> expected_names{kExtensionA, kExtensionB,
-                                            kExtensionC};
-    EXPECT_EQ(GetPinnedExtensionNames(), expected_names);
-  }
-
-  // Unpinning the middle extension should remove it from the toolbar without
-  // affecting the order of the other pinned extensions.
-  {
-    ClickPinButton(items.at(1));
-    std::vector<std::string> expected_names{kExtensionA, kExtensionC};
-    EXPECT_EQ(GetPinnedExtensionNames(), expected_names);
-  }
-
-  // Pinning an extension should add it to the right of the current pinned
-  // extensions, even if it was pinned and unpinned previously.
-  {
-    ClickPinButton(items.at(1));
-    std::vector<std::string> expected_names{kExtensionA, kExtensionC,
-                                            kExtensionB};
-    EXPECT_EQ(GetPinnedExtensionNames(), expected_names);
-  }
-}
-
-TEST_F(ExtensionsMenuMainPageViewUnitTest,
-       PinnedExtensionAppearsInAnotherWindow) {
-  const std::string& extension_id = InstallExtension("Extension")->id();
-
-  ShowMenu();
-
-  AdditionalBrowser browser2(
-      CreateBrowser(browser()->profile(), browser()->type(),
-                    /* hosted_app */ false, /* browser_window */ nullptr));
-
-  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
-  ASSERT_TRUE(menu_item);
-  ClickPinButton(menu_item);
-
-  // Window that was already open gets the pinned extension.
-  EXPECT_TRUE(
-      browser2.extensions_container()->IsActionVisibleOnToolbar(extension_id));
-
-  AdditionalBrowser browser3(
-      CreateBrowser(browser()->profile(), browser()->type(),
-                    /* hosted_app */ false, /* browser_window */ nullptr));
-
-  // Brand-new window also gets the pinned extension.
-  EXPECT_TRUE(
-      browser3.extensions_container()->IsActionVisibleOnToolbar(extension_id));
-}
-
-// Verifies the extension site permissions button opens the site permissions
-// page corresponding to the extension.
-TEST_F(ExtensionsMenuMainPageViewUnitTest,
-       SitePermissionsButtonOpensSubpageForCorrectExtension) {
-  auto extensionA =
-      InstallExtensionWithHostPermissions("Extension A", {"<all_urls>"});
-  InstallExtensionWithHostPermissions("Extension B", {"<all_urls>"});
+       SitePermissionsButton_CustomizeByExtension) {
+  auto extension = InstallExtension("Extension A");
+  auto extension_with_permissions =
+      InstallExtensionWithHostPermissions("Extension B", {"<all_urls>"});
+  auto enterprise_extension =
+      InstallEnterpriseExtension("Extension C",
+                                 /*host_permissions=*/{"<all_urls>"});
 
   // Navigate to a page that extensions request access, and open the menu.
-  web_contents_tester()->NavigateAndCommit(GURL("http://www.example.com"));
+  const GURL url("http://www.example.com");
+  auto url_origin = url::Origin::Create(url);
+  web_contents_tester()->NavigateAndCommit(url);
   ShowMenu();
 
+  // Verify menu items are in the expected order, so we can check their site
+  // permissions button correctly.
   std::vector<ExtensionMenuItemView*> items = menu_items();
-  ASSERT_EQ(items.size(), 2u);
-  EXPECT_EQ(items[0]->view_controller()->GetId(), extensionA->id());
+  ASSERT_EQ(items.size(), 3u);
+  ExtensionMenuItemView* extension_item = items[0];
+  ExtensionMenuItemView* extension_with_permissions_item = items[1];
+  ExtensionMenuItemView* enterprise_extension_item = items[2];
+  ASSERT_EQ(extension_item->view_controller()->GetId(), extension->id());
+  ASSERT_EQ(extension_with_permissions_item->view_controller()->GetId(),
+            extension_with_permissions->id());
+  ASSERT_EQ(enterprise_extension_item->view_controller()->GetId(),
+            enterprise_extension->id());
 
-  // Site permissions button should be visible for both extensions.
-  EXPECT_TRUE(items[0]->site_permissions_button_for_testing()->GetVisible());
-  EXPECT_TRUE(items[1]->site_permissions_button_for_testing()->GetVisible());
+  // By default, site settings is set to "customize by extension".
+  PermissionsManager* permissions_manager = PermissionsManager::Get(profile());
+  EXPECT_EQ(permissions_manager->GetUserSiteSetting(url_origin),
+            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
 
-  // Clicking on an extension's site permission button should open its site
-  // permission page in the menu.
-  ClickSitePermissionsButton(items[0]);
+  // Site permissions button should be visible for all extensions, and enabled
+  // only for the extension with host permissions.
+  EXPECT_TRUE(
+      extension_item->site_permissions_button_for_testing()->GetVisible());
+  EXPECT_FALSE(
+      extension_item->site_permissions_button_for_testing()->GetEnabled());
+  EXPECT_TRUE(
+      extension_with_permissions_item->site_permissions_button_for_testing()
+          ->GetVisible());
+  EXPECT_TRUE(
+      extension_with_permissions_item->site_permissions_button_for_testing()
+          ->GetEnabled());
+  EXPECT_TRUE(enterprise_extension_item->site_permissions_button_for_testing()
+                  ->GetVisible());
+  EXPECT_FALSE(enterprise_extension_item->site_permissions_button_for_testing()
+                   ->GetEnabled());
+
+  // Clicking on an extension's site permission disabled button should do
+  // nothing.
+  ClickSitePermissionsButton(extension_item);
+  EXPECT_TRUE(main_page());
+  EXPECT_FALSE(site_permissions_page());
+
+  // Clicking on an extension's site permission enabled button should open
+  // its site permission page in the menu.
+  ClickSitePermissionsButton(extension_with_permissions_item);
+  EXPECT_FALSE(main_page());
   ExtensionsMenuSitePermissionsPageView* page = site_permissions_page();
   ASSERT_TRUE(page);
-  EXPECT_EQ(page->extension_id(), extensionA->id());
+  EXPECT_EQ(page->extension_id(), extension_with_permissions->id());
+}
+
+// Verifies that only the permission button of enterprise extensions is visible,
+// but disabled, when user blocked all extensions on a site.
+TEST_F(ExtensionsMenuMainPageViewUnitTest,
+       SitePermissionsButton_BlockAllExtensions) {
+  auto extension =
+      InstallExtensionWithHostPermissions("Extension A", {"<all_urls>"});
+  auto enterprise_extension =
+      InstallEnterpriseExtension("Extension B",
+                                 /*host_permissions=*/{});
+  auto enterprise_extension_with_permissions =
+      InstallEnterpriseExtension("Extension C",
+                                 /*host_permissions=*/{"<all_urls>"});
+
+  // Navigate to a page that extensions request access, and open the menu.
+  const GURL url(u"http://www.example.com");
+  auto url_origin = url::Origin::Create(url);
+  web_contents_tester()->NavigateAndCommit(url);
+  ShowMenu();
+
+  // Verify menu items are in the expected order, so we can check their site
+  // permissions button correctly.
+  std::vector<ExtensionMenuItemView*> items = menu_items();
+  ASSERT_EQ(items.size(), 3u);
+  ExtensionMenuItemView* extension_item = items[0];
+  ExtensionMenuItemView* enterprise_extension_item = items[1];
+  ExtensionMenuItemView* enterprise_extension_with_permissions_item = items[2];
+  ASSERT_EQ(extension_item->view_controller()->GetId(), extension->id());
+  ASSERT_EQ(enterprise_extension_item->view_controller()->GetId(),
+            enterprise_extension->id());
+  ASSERT_EQ(
+      enterprise_extension_with_permissions_item->view_controller()->GetId(),
+      enterprise_extension_with_permissions->id());
+
+  // Update site setting to "block all extensions".
+  PermissionsManager* permissions_manager = PermissionsManager::Get(profile());
+  extensions::PermissionsManagerWaiter waiter(permissions_manager);
+  permissions_manager->UpdateUserSiteSetting(
+      url_origin, PermissionsManager::UserSiteSetting::kBlockAllExtensions);
+  waiter.WaitForUserPermissionsSettingsChange();
+
+  // Since the user blocked extensions on this site, most extensions
+  // shouldn't display the site permissions button at all. There's an
+  // exception for the policy-installed extension, since it can still run
+  // on the site (because enterprise-installed extensions take priority over
+  // user settings). For this extension, the control should be visible
+  // (so the user can see that it can run), but not clickable (bceause
+  // the user can't modify the settings).
+  EXPECT_FALSE(
+      extension_item->site_permissions_button_for_testing()->GetVisible());
+  EXPECT_FALSE(
+      extension_item->site_permissions_button_for_testing()->GetEnabled());
+  EXPECT_FALSE(enterprise_extension_item->site_permissions_button_for_testing()
+                   ->GetVisible());
+  EXPECT_FALSE(enterprise_extension_item->site_permissions_button_for_testing()
+                   ->GetEnabled());
+  EXPECT_TRUE(enterprise_extension_with_permissions_item
+                  ->site_permissions_button_for_testing()
+                  ->GetVisible());
+  EXPECT_FALSE(enterprise_extension_with_permissions_item
+                   ->site_permissions_button_for_testing()
+                   ->GetEnabled());
+
+  // Clicking on any of the button should do nothing since it is disabled.
+  ClickSitePermissionsButton(enterprise_extension_with_permissions_item);
+  EXPECT_TRUE(main_page());
+  EXPECT_FALSE(site_permissions_page());
 }
 
 TEST_F(ExtensionsMenuMainPageViewUnitTest,
@@ -378,28 +345,25 @@
   auto extension_id = InstallExtension(kName)->id();
 
   ShowMenu();
-
-  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
   EXPECT_EQ(menu_items().size(), 1u);
-  ClickPinButton(menu_item);
 
   DisableExtension(extension_id);
   LayoutMenuIfNecessary();
-  WaitForAnimation();
 
   EXPECT_EQ(menu_items().size(), 0u);
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
 
   EnableExtension(extension_id);
   LayoutMenuIfNecessary();
-  WaitForAnimation();
 
   EXPECT_EQ(menu_items().size(), 1u);
-  EXPECT_EQ(GetPinnedExtensionNames(), std::vector<std::string>{kName});
 }
 
-// Tests that when an extension is reloaded it remains visible in the toolbar
-// and extensions menu.
+// Tests that when an extension is reloaded it remains visible in the extensions
+// menu.
+// TODO(crbug.com/1390952): Verify context menu button shows the correct icon as
+// pinned state is also preserved when a reload happens. Add this functionality
+// when showing pin icon instead of context menu when extension is pinned is
+// added.
 TEST_F(ExtensionsMenuMainPageViewUnitTest, ReloadExtension) {
   // The extension must have a manifest to be reloaded.
   extensions::TestExtensionDir extension_directory;
@@ -414,14 +378,8 @@
       loader.LoadExtension(extension_directory.UnpackedPath());
 
   ShowMenu();
-
-  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
   EXPECT_EQ(menu_items().size(), 1u);
 
-  ClickPinButton(menu_item);
-  EXPECT_TRUE(
-      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
-
   // Reload the extension.
   extensions::TestExtensionRegistryObserver registry_observer(
       extensions::ExtensionRegistry::Get(profile()));
@@ -429,15 +387,12 @@
   ASSERT_TRUE(registry_observer.WaitForExtensionLoaded());
   LayoutMenuIfNecessary();
 
-  // Verify the extension is visible in the menu and on the toolbar.
-  menu_item = GetOnlyMenuItem();
+  // Verify the extension is visible in the menu.
   EXPECT_EQ(menu_items().size(), 1u);
-  EXPECT_TRUE(
-      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
 }
 
 // Tests that a when an extension is reloaded with manifest errors, and
-// therefore fails to be loaded into Chrome, it's removed from the toolbar and
+// therefore fails to be loaded into Chrome, it's removed from the
 // extensions menu.
 TEST_F(ExtensionsMenuMainPageViewUnitTest, ReloadExtensionFailed) {
   extensions::TestExtensionDir extension_directory;
@@ -452,14 +407,8 @@
       loader.LoadExtension(extension_directory.UnpackedPath());
 
   ShowMenu();
-
-  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
   EXPECT_EQ(menu_items().size(), 1u);
 
-  ClickPinButton(menu_item);
-  EXPECT_TRUE(
-      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
-
   // Replace the extension's valid manifest with one containing errors. In this
   // case, 'version' keys is missing.
   constexpr char kManifestWithErrors[] = R"({
@@ -476,9 +425,6 @@
   // Verify the extension is no longer visible in the menu or on the toolbar
   // since it was removed.
   EXPECT_EQ(menu_items().size(), 0u);
-  for (views::View* child : extensions_container()->children()) {
-    EXPECT_FALSE(views::IsViewClass<ToolbarActionView>(child));
-  }
 }
 
 // Tests that extension's site permission button is always hidden when site is
@@ -523,51 +469,3 @@
   EXPECT_FALSE(
       menu_items()[1]->site_permissions_button_for_testing()->GetVisible());
 }
-
-// Test that an enterprise extension's site permissions button is shown based on
-// the user site setting and extension site interaction.
-TEST_F(ExtensionsMenuMainPageViewUnitTest, EnterpriseExtension) {
-  constexpr char kExtension[] = "Enterprise extension";
-  constexpr char kExtensionWithPermissions[] =
-      "Enterprise extension with host permissions";
-  InstallEnterpriseExtension(kExtension, /*host_permissions=*/{});
-  InstallEnterpriseExtension(kExtensionWithPermissions,
-                             /*host_permissions=*/{"<all_urls>"});
-
-  const GURL url(u"http://www.example.com");
-  auto url_origin = url::Origin::Create(url);
-  web_contents_tester()->NavigateAndCommit(url);
-
-  // By default, user site setting is set to "customize by extension".
-  PermissionsManager* permissions_manager = PermissionsManager::Get(profile());
-  EXPECT_EQ(permissions_manager->GetUserSiteSetting(url_origin),
-            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
-
-  // Open menu and verify extensions are in the expected order.
-  ShowMenu();
-  ASSERT_EQ(menu_items().size(), 2u);
-  std::vector<std::string> expected_items{kExtension,
-                                          kExtensionWithPermissions};
-  EXPECT_EQ(GetNamesFromMenuItems(menu_items()), expected_items);
-
-  // Enterprise extension's site permission button is always visible when user
-  // can customize site access by extension.
-  EXPECT_TRUE(
-      menu_items()[0]->site_permissions_button_for_testing()->GetVisible());
-  EXPECT_TRUE(
-      menu_items()[1]->site_permissions_button_for_testing()->GetVisible());
-
-  extensions::PermissionsManagerWaiter waiter(
-      PermissionsManager::Get(browser()->profile()));
-  permissions_manager->UpdateUserSiteSetting(
-      url_origin, PermissionsManager::UserSiteSetting::kBlockAllExtensions);
-  waiter.WaitForUserPermissionsSettingsChange();
-
-  // Enterprise extension's site permission button is still visible when
-  // user has blocked all extensions but enterprise extension still has access
-  // (policy his priority over user settings).
-  EXPECT_FALSE(
-      menu_items()[0]->site_permissions_button_for_testing()->GetVisible());
-  EXPECT_TRUE(
-      menu_items()[1]->site_permissions_button_for_testing()->GetVisible());
-}
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc
index 45cfe0b2..21562d6a 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc
@@ -29,7 +29,9 @@
   ExtensionsSitePermissionsPageViewUnitTest& operator=(
       const ExtensionsSitePermissionsPageViewUnitTest&) = delete;
 
-  // Opens menu and navigates to site permissions page for `extension_id`.
+  // Opens menu and navigates to site permissions page for `extension_id`. This
+  // will CHECK if extension cannot have a site permissions page (e.g
+  // restricted site).
   void ShowSitePermissionsPage(extensions::ExtensionId extension_id);
 
   // Returns whether me menu has the main page opened.
@@ -43,6 +45,9 @@
   // toolbar.
   std::vector<extensions::ExtensionId> GetExtensionsShowingRequests();
 
+  // Navigates to `string_url`.
+  void NavigateAndCommit(const std::string& string_url);
+
   // Since this is a unittest, the extensions menu widget sometimes needs a
   // nudge to re-layout the views.
   void LayoutMenuIfNecessary();
@@ -90,6 +95,13 @@
       ->GetExtensionIdsForTesting();
 }
 
+void ExtensionsSitePermissionsPageViewUnitTest::NavigateAndCommit(
+    const std::string& string_url) {
+  const GURL url(string_url);
+  web_contents_tester_->NavigateAndCommit(url);
+  WaitForAnimation();
+}
+
 void ExtensionsSitePermissionsPageViewUnitTest::LayoutMenuIfNecessary() {
   menu_coordinator()->GetExtensionsMenuWidget()->LayoutRootViewIfNecessary();
 }
@@ -119,8 +131,10 @@
 
 TEST_F(ExtensionsSitePermissionsPageViewUnitTest,
        AddAndRemoveExtensionWhenSitePermissionsPageIsOpen) {
-  auto extensionA = InstallExtension("A Extension");
+  auto extensionA =
+      InstallExtensionWithHostPermissions("A Extension", {"<all_urls>"});
 
+  NavigateAndCommit("http://www.url.com");
   ShowSitePermissionsPage(extensionA->id());
 
   // Verify site permissions page is open for extension A.
@@ -128,7 +142,8 @@
 
   // Adding a new extension doesn't affect the opened site permissions page for
   // extension A.
-  auto extensionB = InstallExtension("B Extension");
+  auto extensionB =
+      InstallExtensionWithHostPermissions("B Extension", {"<all_urls>"});
   EXPECT_TRUE(IsSitePermissionsPageOpened(extensionA->id()));
 
   // Removing extension B doesn't affect the opened site permissions page for
@@ -146,8 +161,10 @@
 // Tests that menu navigates back to the main page when an extension, whose site
 // permissions page is open, is disabled.
 TEST_F(ExtensionsSitePermissionsPageViewUnitTest, DisableAndEnableExtension) {
-  auto extension = InstallExtension("Test Extension");
+  auto extension =
+      InstallExtensionWithHostPermissions("Test Extension", {"<all_urls>"});
 
+  NavigateAndCommit("http://www.url.com");
   ShowSitePermissionsPage(extension->id());
   EXPECT_TRUE(IsSitePermissionsPageOpened(extension->id()));
 
@@ -167,13 +184,17 @@
   constexpr char kManifest[] = R"({
         "name": "Test Extension",
         "version": "1",
-        "manifest_version": 3
+        "manifest_version": 3,
+        "host_permissions": [
+          "<all_urls>"
+        ]
       })";
   extension_directory.WriteManifest(kManifest);
   extensions::ChromeTestExtensionLoader loader(profile());
   scoped_refptr<const extensions::Extension> extension =
       loader.LoadExtension(extension_directory.UnpackedPath());
 
+  NavigateAndCommit("http://www.url.com");
   ShowSitePermissionsPage(extension->id());
   EXPECT_TRUE(IsSitePermissionsPageOpened(extension->id()));
 
@@ -191,9 +212,6 @@
 // Tests that toggling the show requests button changes whether an extension can
 // show site access requests in the toolbar, and the UI is properly updated.
 TEST_F(ExtensionsSitePermissionsPageViewUnitTest, ShowRequestsTogglePressed) {
-  content::WebContentsTester* web_contents_tester =
-      AddWebContentsAndGetTester();
-
   auto extensionA =
       InstallExtensionWithHostPermissions("Extension A", {"<all_urls>"});
   auto extensionB =
@@ -201,10 +219,7 @@
   WithholdHostPermissions(extensionA.get());
   WithholdHostPermissions(extensionB.get());
 
-  const GURL url("http://www.url.com");
-  web_contents_tester->NavigateAndCommit(url);
-  WaitForAnimation();
-
+  NavigateAndCommit("http://www.url.com");
   ShowSitePermissionsPage(extensionA->id());
   EXPECT_TRUE(IsSitePermissionsPageOpened(extensionA->id()));
 
@@ -233,17 +248,11 @@
 // access requests in the toolbar changes while the menu is open.
 TEST_F(ExtensionsSitePermissionsPageViewUnitTest,
        ShowRequestsPrefChangedWithMenuOpen) {
-  content::WebContentsTester* web_contents_tester =
-      AddWebContentsAndGetTester();
-
   auto extension =
       InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});
   WithholdHostPermissions(extension.get());
 
-  const GURL url("http://www.url.com");
-  web_contents_tester->NavigateAndCommit(url);
-  WaitForAnimation();
-
+  NavigateAndCommit("http://www.url.com");
   ShowSitePermissionsPage(extension->id());
   EXPECT_TRUE(IsSitePermissionsPageOpened(extension->id()));
 
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
index e999cf6..8d1e081 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
@@ -306,8 +306,7 @@
   // be added to the view hierarchy, which takes ownership.
   auto* item = new ExtensionMenuItemView(
       browser_, std::move(controller),
-      extensions_container_->CanShowActionsInToolbar(),
-      /*is_site_permissions_button_visible=*/false);
+      extensions_container_->CanShowActionsInToolbar());
   extensions_menu_items_.insert(item);
   InsertMenuItem(item);
   // Sanity check that the item was added.
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc
index 91dbc2b..848a9f6 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc
@@ -30,6 +30,15 @@
 using PermissionsManager = extensions::PermissionsManager;
 using SitePermissionsHelper = extensions::SitePermissionsHelper;
 
+// Returns the extension for `extension_id`.
+const extensions::Extension* GetExtension(
+    Browser* browser,
+    extensions::ExtensionId extension_id) {
+  return extensions::ExtensionRegistry::Get(browser->profile())
+      ->enabled_extensions()
+      .GetByID(extension_id);
+}
+
 // Returns sorted extension ids based on their extensions name.
 std::vector<std::string> SortExtensionsByName(
     ToolbarActionsModel& toolbar_model) {
@@ -132,6 +141,62 @@
   }
 }
 
+// Returns whether user can select the site access for `extension` on
+// `web_contents`.
+bool CanUserCustomizeExtensionSiteAccess(
+    const extensions::Extension& extension,
+    Profile& profile,
+    const ToolbarActionsModel& toolbar_model,
+    content::WebContents& web_contents) {
+  const GURL& url = web_contents.GetLastCommittedURL();
+  if (toolbar_model.IsRestrictedUrl(url)) {
+    // We don't allow customization of restricted sites (e.g.
+    // chrome://settings).
+    return false;
+  }
+
+  bool enterprise_forced_access = HasEnterpriseForcedAccess(extension, profile);
+  if (enterprise_forced_access) {
+    // Users can't customize the site access of enterprise-installed extensions.
+    return false;
+  }
+
+  // The extension wants site access if it at least wants "on click" access.
+  auto* permissions_manager = extensions::PermissionsManager::Get(&profile);
+  bool extension_wants_access = permissions_manager->CanUserSelectSiteAccess(
+      extension, url, PermissionsManager::UserSiteAccess::kOnClick);
+  if (!extension_wants_access) {
+    // Users can't customize site access of extensions that don't want access to
+    // begin with.
+    return false;
+  }
+
+  // Users can only customize site access when they have allowed all extensions
+  // to be customizable on the site.
+  return permissions_manager->GetUserSiteSetting(
+             web_contents.GetPrimaryMainFrame()->GetLastCommittedOrigin()) ==
+         PermissionsManager::UserSiteSetting::kCustomizeByExtension;
+}
+
+// Returns the state for the `extension`'s site permissions button.
+ExtensionMenuItemView::SitePermissionsButtonState GetSitePermissionsButtonState(
+    const extensions::Extension& extension,
+    Profile& profile,
+    const ToolbarActionsModel& toolbar_model,
+    content::WebContents& web_contents) {
+  bool is_site_permissions_button_visible = IsSitePermissionsButtonVisible(
+      extension, profile, toolbar_model, web_contents);
+  if (!is_site_permissions_button_visible) {
+    return ExtensionMenuItemView::SitePermissionsButtonState::kHidden;
+  }
+
+  bool is_site_permissions_button_enabled = CanUserCustomizeExtensionSiteAccess(
+      extension, profile, toolbar_model, web_contents);
+  return is_site_permissions_button_enabled
+             ? ExtensionMenuItemView::SitePermissionsButtonState::kEnabled
+             : ExtensionMenuItemView::SitePermissionsButtonState::kDisabled;
+}
+
 }  // namespace
 
 ExtensionsMenuViewController::ExtensionsMenuViewController(
@@ -173,6 +238,10 @@
 
 void ExtensionsMenuViewController::OpenSitePermissionsPage(
     extensions::ExtensionId extension_id) {
+  CHECK(CanUserCustomizeExtensionSiteAccess(
+      *GetExtension(browser_, extension_id), *browser_->profile(),
+      *toolbar_model_, *GetActiveWebContents()));
+
   const int icon_size = ChromeLayoutProvider::Get()->GetDistanceMetric(
       DISTANCE_EXTENSIONS_MENU_EXTENSION_ICON_SIZE);
   std::unique_ptr<ExtensionActionViewController> action_controller =
@@ -238,10 +307,10 @@
               .GetByID(menu_item->view_controller()->GetId());
       CHECK(extension);
 
-      bool is_site_permissions_button_visible = IsSitePermissionsButtonVisible(
-          *extension, *browser_->profile(), *toolbar_model_,
-          *GetActiveWebContents());
-      menu_item->Update(is_site_permissions_button_visible);
+      ExtensionMenuItemView::SitePermissionsButtonState
+          site_permissions_button_state = GetSitePermissionsButtonState(
+              *extension, *browser_->profile(), *toolbar_model_, *web_contents);
+      menu_item->Update(site_permissions_button_state);
     }
   }
 }
@@ -264,14 +333,13 @@
   std::unique_ptr<ExtensionActionViewController> action_controller =
       ExtensionActionViewController::Create(action_id, browser_,
                                             extensions_container_);
-  bool is_site_permissions_button_visible = IsSitePermissionsButtonVisible(
-      *action_controller->extension(), *browser_->profile(), *toolbar_model_,
-      *GetActiveWebContents());
+  ExtensionMenuItemView::SitePermissionsButtonState
+      site_permissions_button_state = GetSitePermissionsButtonState(
+          *action_controller->extension(), *browser_->profile(),
+          *toolbar_model_, *GetActiveWebContents());
 
-  main_page->CreateAndInsertMenuItem(
-      std::move(action_controller), action_id,
-      extensions_container_->CanShowActionsInToolbar(),
-      is_site_permissions_button_visible, index);
+  main_page->CreateAndInsertMenuItem(std::move(action_controller), action_id,
+                                     site_permissions_button_state, index);
 
   // TODO(crbug.com/1390952): Update requests access section once such section
   // is implemented (if the extension added requests site access, it needs to be
@@ -412,7 +480,6 @@
 
 void ExtensionsMenuViewController::PopulateMainPage(
     ExtensionsMenuMainPageView* main_page) {
-  bool allow_pinning = extensions_container_->CanShowActionsInToolbar();
   std::vector<std::string> sorted_ids = SortExtensionsByName(*toolbar_model_);
   for (size_t i = 0; i < sorted_ids.size(); ++i) {
     // TODO(emiliapaz): Under MVC architecture, view should not own the view
@@ -421,12 +488,14 @@
     std::unique_ptr<ExtensionActionViewController> action_controller =
         ExtensionActionViewController::Create(sorted_ids[i], browser_,
                                               extensions_container_);
-    bool is_site_permissions_button_visible = IsSitePermissionsButtonVisible(
-        *action_controller->extension(), *browser_->profile(), *toolbar_model_,
-        *GetActiveWebContents());
+    ExtensionMenuItemView::SitePermissionsButtonState
+        site_permissions_button_state = GetSitePermissionsButtonState(
+            *action_controller->extension(), *browser_->profile(),
+            *toolbar_model_, *GetActiveWebContents());
+
     main_page->CreateAndInsertMenuItem(std::move(action_controller),
-                                       sorted_ids[i], allow_pinning,
-                                       is_site_permissions_button_visible, i);
+                                       sorted_ids[i],
+                                       site_permissions_button_state, i);
   }
 }
 
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container_unittest.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container_unittest.cc
index bcad771..e4b23aa 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container_unittest.cc
@@ -6,16 +6,47 @@
 
 #include "base/json/json_reader.h"
 #include "base/ranges/algorithm.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "extensions/browser/pref_names.h"
+#include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/common/extension_id.h"
+#include "extensions/test/test_extension_dir.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
 
+namespace {
+
+// A scoper that manages a Browser instance created by BrowserWithTestWindowTest
+// beyond the default instance it creates in SetUp.
+class AdditionalBrowser {
+ public:
+  explicit AdditionalBrowser(std::unique_ptr<Browser> browser)
+      : browser_(std::move(browser)),
+        browser_view_(BrowserView::GetBrowserViewForBrowser(browser_.get())) {}
+
+  ~AdditionalBrowser() {
+    // Tear down `browser_`, similar to TestWithBrowserView::TearDown.
+    browser_.release();
+    browser_view_->GetWidget()->CloseNow();
+  }
+
+  ExtensionsToolbarContainer* extensions_container() {
+    return browser_view_->toolbar()->extensions_container();
+  }
+
+ private:
+  std::unique_ptr<Browser> browser_;
+  raw_ptr<BrowserView> browser_view_;
+};
+
+}  // namespace
+
 class ExtensionsToolbarContainerUnitTest : public ExtensionsToolbarUnitTest {
  public:
   ExtensionsToolbarContainerUnitTest();
@@ -138,6 +169,118 @@
       drag_view, gfx::Point(), gfx::Point()));
 }
 
+// Tests that when an extension is reloaded it remains visible in the toolbar.
+TEST_F(ExtensionsToolbarContainerUnitTest, ReloadExtensionKeepsPinnedState) {
+  // The extension must have a manifest to be reloaded.
+  extensions::TestExtensionDir extension_directory;
+  constexpr char kManifest[] = R"({
+        "name": "Test Extension",
+        "version": "1",
+        "manifest_version": 3
+      })";
+  extension_directory.WriteManifest(kManifest);
+  extensions::ChromeTestExtensionLoader loader(profile());
+  scoped_refptr<const extensions::Extension> extension =
+      loader.LoadExtension(extension_directory.UnpackedPath());
+
+  // By default, extension on installation is unpinned.
+  EXPECT_FALSE(
+      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
+
+  // Pin extension and verify it is visible on the toolbar.
+  auto* toolbar_model = ToolbarActionsModel::Get(profile());
+  ASSERT_TRUE(toolbar_model);
+  toolbar_model->SetActionVisibility(extension->id(), true);
+  EXPECT_TRUE(
+      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
+
+  // Reload the extension.
+  extensions::TestExtensionRegistryObserver registry_observer(
+      extensions::ExtensionRegistry::Get(profile()));
+  ReloadExtension(extension->id());
+  ASSERT_TRUE(registry_observer.WaitForExtensionLoaded());
+  WaitForAnimation();
+
+  // Verify the extension is visible on the toolbar.
+  EXPECT_TRUE(
+      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
+}
+
+// Tests that a when an extension is reloaded with manifest errors, and
+// therefore fails to be loaded into Chrome, it's removed from the toolbar.
+TEST_F(ExtensionsToolbarContainerUnitTest, ReloadExtensionFailed) {
+  extensions::TestExtensionDir extension_directory;
+  constexpr char kManifest[] = R"({
+        "name": "Test Extension",
+        "version": "1",
+        "manifest_version": 3
+      })";
+  extension_directory.WriteManifest(kManifest);
+  extensions::ChromeTestExtensionLoader loader(profile());
+  scoped_refptr<const extensions::Extension> extension =
+      loader.LoadExtension(extension_directory.UnpackedPath());
+
+  // By default, extension on installation is unpinned.
+  EXPECT_FALSE(
+      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
+
+  // Pin extension and verify it is visible on the toolbar.
+  auto* toolbar_model = ToolbarActionsModel::Get(profile());
+  ASSERT_TRUE(toolbar_model);
+  toolbar_model->SetActionVisibility(extension->id(), true);
+  EXPECT_TRUE(
+      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
+
+  // Replace the extension's valid manifest with one containing errors. In this
+  // case, 'version' keys is missing.
+  constexpr char kManifestWithErrors[] = R"({
+        "name": "Test",
+        "manifest_version": 3,
+      })";
+  extension_directory.WriteManifest(kManifestWithErrors);
+
+  // Reload the extension. It should fail due to the manifest errors.
+  extension_service()->ReloadExtensionWithQuietFailure(extension->id());
+  base::RunLoop().RunUntilIdle();
+  WaitForAnimation();
+
+  // Verify the extension is no longer visible on the toolbar.
+  EXPECT_FALSE(
+      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
+}
+
+TEST_F(ExtensionsToolbarContainerUnitTest,
+       PinnedExtensionAppearsInAnotherWindow) {
+  const std::string& extension_id = InstallExtension("Extension")->id();
+
+  AdditionalBrowser browser2(
+      CreateBrowser(browser()->profile(), browser()->type(),
+                    /* hosted_app */ false, /* browser_window */ nullptr));
+
+  // Verify extension is unpinned in both windows.
+  EXPECT_FALSE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
+  EXPECT_FALSE(
+      browser2.extensions_container()->IsActionVisibleOnToolbar(extension_id));
+
+  // Pin extension in one window.
+  auto* toolbar_model = ToolbarActionsModel::Get(profile());
+  ASSERT_TRUE(toolbar_model);
+  toolbar_model->SetActionVisibility(extension_id, true);
+
+  // Both windows open get the pinned extension.
+  EXPECT_TRUE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
+  EXPECT_TRUE(
+      browser2.extensions_container()->IsActionVisibleOnToolbar(extension_id));
+
+  AdditionalBrowser browser3(
+      CreateBrowser(browser()->profile(), browser()->type(),
+                    /* hosted_app */ false, /* browser_window */ nullptr));
+
+  // Brand-new window also gets the pinned extension.
+  EXPECT_TRUE(
+      browser3.extensions_container()->IsActionVisibleOnToolbar(extension_id));
+}
+
 TEST_F(ExtensionsToolbarContainerUnitTest,
        PinnedExtensionsReorderOnPrefChange) {
   constexpr char kExtensionAName[] = "A Extension";
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 b586884..21cb159bf 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
@@ -242,12 +242,12 @@
   auto* progress_view = view_->GetDownloadProgressMessageViewForTesting();
   EXPECT_NE(nullptr, progress_view);
 
-  // The message and title labels should each have fired an accessibility event
-  // as a result of the introductory/set-up text being displayed. Because the
-  // download has not started, there should be no event from the download
-  // progress label.
-  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kTextChanged, title_view));
-  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kTextChanged, message_view));
+  // Views should only fire property-change events when the property changes;
+  // not when a value is initialized. As a result, there should not be any
+  // text-changed accessibility fired as a result of the introductory/set-up
+  // text being displayed.
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kTextChanged, title_view));
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kTextChanged, message_view));
   EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kTextChanged, progress_view));
 
   counter.ResetAllCounts();
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index cc09e0be..54a2c46 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -216,7 +216,6 @@
 #include "ash/webui/print_management/print_management_ui.h"
 #include "ash/webui/print_management/url_constants.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"  // nogncheck
-#include "ash/webui/projector_app/trusted_projector_annotator_ui.h"
 #include "ash/webui/projector_app/trusted_projector_ui.h"
 #include "ash/webui/scanning/scanning_ui.h"
 #include "ash/webui/scanning/url_constants.h"
@@ -501,13 +500,6 @@
                                      Profile::FromWebUI(web_ui)->GetPrefs());
 }
 
-template <>
-WebUIController* NewWebUI<ash::TrustedProjectorAnnotatorUI>(WebUI* web_ui,
-                                                            const GURL& url) {
-  return new ash::TrustedProjectorAnnotatorUI(
-      web_ui, url, Profile::FromWebUI(web_ui)->GetPrefs());
-}
-
 void BindPrintManagement(
     Profile* profile,
     mojo::PendingReceiver<
@@ -1037,10 +1029,6 @@
       IsProjectorAppEnabled(profile)) {
     return &NewWebUI<ash::TrustedProjectorUI>;
   }
-  if (url.host_piece() == ash::kChromeUIProjectorAnnotatorHost &&
-      IsProjectorAppEnabled(profile)) {
-    return &NewWebUI<ash::TrustedProjectorAnnotatorUI>;
-  }
   if (url.host_piece() == ash::eche_app::kChromeUIEcheAppHost &&
       base::FeatureList::IsEnabled(ash::features::kEcheSWA)) {
     return &NewWebUI<ash::eche_app::EcheAppUI>;
diff --git a/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.cc b/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.cc
index 49ce2f53..ed637b7 100644
--- a/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.cc
+++ b/chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.cc
@@ -49,6 +49,8 @@
   source->AddString(
       "chromeRefresh2023Attribute",
       features::IsChromeRefresh2023() ? "chrome-refresh-2023" : "");
+  source->AddBoolean("isHistoryClustersImageCover",
+                     history_clusters::GetConfig().images_cover);
 
   static constexpr webui::LocalizedString kHistoryClustersStrings[] = {
       {"actionMenuDescription", IDS_HISTORY_CLUSTERS_ACTION_MENU_DESCRIPTION},
diff --git a/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc b/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
index c80c431..2bd1bad 100644
--- a/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
+++ b/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
@@ -182,8 +182,9 @@
     result->keyword = base::UTF16ToUTF8(input.keyword);
     result->duplicates = static_cast<int32_t>(input.duplicate_matches.size());
     result->from_previous = input.from_previous;
-    const OmniboxAction* action = input.GetPrimaryAction();
-    result->pedal_id = action ? action->GetID() : 0;
+    const auto* pedal = OmniboxPedal::FromAction(input.GetPrimaryAction());
+    result->pedal_id =
+        pedal == nullptr ? 0 : static_cast<int32_t>(pedal->PedalId());
     result->additional_info =
         mojo::ConvertTo<std::vector<mojom::AutocompleteAdditionalInfoPtr>>(
             input.additional_info);
diff --git a/chrome/browser/ui/webui/settings/password_manager_handler.cc b/chrome/browser/ui/webui/settings/password_manager_handler.cc
new file mode 100644
index 0000000..0ce557ba
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/password_manager_handler.cc
@@ -0,0 +1,56 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/settings/password_manager_handler.h"
+
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/chrome_pages.h"
+
+namespace settings {
+
+namespace {
+
+// WARNING: Keep synced with
+// chrome/browser/resources/settings/autofill_page/password_manager_proxy.ts.
+enum class PasswordManagerPage {
+  kPasswords = 0,
+  kCheckup,
+  kLastItem = kCheckup
+};
+
+}  // namespace
+
+PasswordManagerHandler::PasswordManagerHandler() = default;
+PasswordManagerHandler::~PasswordManagerHandler() = default;
+
+void PasswordManagerHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "showPasswordManager",
+      base::BindRepeating(&PasswordManagerHandler::HandleShowPasswordManager,
+                          base::Unretained(this)));
+}
+
+void PasswordManagerHandler::HandleShowPasswordManager(
+    const base::Value::List& args) {
+  AllowJavascript();
+  CHECK_EQ(1U, args.size());
+  int page = args[0].GetInt();
+
+  Browser* current_broswer =
+      chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+  CHECK(current_broswer);
+
+  switch (PasswordManagerPage(page)) {
+    case PasswordManagerPage::kPasswords:
+      chrome::ShowPasswordManager(current_broswer);
+      return;
+    case PasswordManagerPage::kCheckup:
+      chrome::ShowPasswordCheck(current_broswer);
+      return;
+    default:
+      NOTREACHED();
+  }
+}
+
+}  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/password_manager_handler.h b/chrome/browser/ui/webui/settings/password_manager_handler.h
new file mode 100644
index 0000000..05fbc99
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/password_manager_handler.h
@@ -0,0 +1,32 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_PASSWORD_MANAGER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_PASSWORD_MANAGER_HANDLER_H_
+
+#include "base/values.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace settings {
+
+// A which enables interactions with Password Manager from settings.
+class PasswordManagerHandler : public content::WebUIMessageHandler {
+ public:
+  explicit PasswordManagerHandler();
+
+  PasswordManagerHandler(const PasswordManagerHandler&) = delete;
+  PasswordManagerHandler& operator=(const PasswordManagerHandler&) = delete;
+
+  ~PasswordManagerHandler() override;
+
+ private:
+  // WebUIMessageHandler:
+  void RegisterMessages() override;
+
+  void HandleShowPasswordManager(const base::Value::List& args);
+};
+
+}  // namespace settings
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_PASSWORD_MANAGER_HANDLER_H_
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 5e3b521..276c72fb 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -309,9 +309,11 @@
 #if BUILDFLAG(IS_MAC)
     {"aboutLearnMoreUpdating", IDS_SETTINGS_ABOUT_PAGE_LEARN_MORE_UPDATING},
 #endif
-    {"getTheMostOutOfProgram", IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM},
-    {"getTheMostOutOfProgramDescription",
-     IDS_SETTINGS_GET_THE_MOST_OUT_OF_PROGRAM_DESCRIPTION},
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+    {"getTheMostOutOfChrome", IDS_SETTINGS_GET_THE_MOST_OUT_OF_CHROME},
+    {"getTheMostOutOfChromeDescription",
+     IDS_SETTINGS_GET_THE_MOST_OUT_OF_CHROME_DESCRIPTION},
+#endif
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
@@ -353,11 +355,10 @@
   html_source->AddString("aboutTermsURL", chrome::kChromeUITermsURL);
   html_source->AddLocalizedString("aboutProductTos",
                                   IDS_ABOUT_TERMS_OF_SERVICE);
-#endif
-
   html_source->AddBoolean(
-      "showGetTheMostOutOfProgramSection",
-      base::FeatureList::IsEnabled(features::kGetTheMostOutOfProgram));
+      "showGetTheMostOutOfChromeSection",
+      base::FeatureList::IsEnabled(features::kGetTheMostOutOfChrome));
+#endif
 }
 
 void AddAppearanceStrings(content::WebUIDataSource* html_source,
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 73a56a7..2bbba4c 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -44,6 +44,7 @@
 #include "chrome/browser/ui/webui/settings/import_data_handler.h"
 #include "chrome/browser/ui/webui/settings/metrics_reporting_handler.h"
 #include "chrome/browser/ui/webui/settings/on_startup_handler.h"
+#include "chrome/browser/ui/webui/settings/password_manager_handler.h"
 #include "chrome/browser/ui/webui/settings/people_handler.h"
 #include "chrome/browser/ui/webui/settings/performance_handler.h"
 #include "chrome/browser/ui/webui/settings/privacy_sandbox_handler.h"
@@ -242,6 +243,10 @@
       std::make_unique<SecurityKeysBioEnrollmentHandler>());
   AddSettingsPageUIHandler(std::make_unique<SecurityKeysPhonesHandler>());
 
+  if (base::FeatureList::IsEnabled(
+          password_manager::features::kPasswordManagerRedesign)) {
+    AddSettingsPageUIHandler(std::make_unique<PasswordManagerHandler>());
+  }
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
   AddSettingsPageUIHandler(std::make_unique<PasskeysHandler>());
 #endif
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 7d6ed65..d6b77497 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1679932723-c05931a5feded7bcf01a5176d8f691d0c8801047.profdata
+chrome-mac-arm-main-1679947177-fb316e82e3ba3c9b100a4d9e87e5e8c76b5abc41.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 2552533..7f485c23 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1679918112-417d920a2afa99c2604497f2f3c2e011ee1bfa58.profdata
+chrome-mac-main-1679939715-f2197e078da171c9a5b41d8a4444f02b40d1bd6a.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index a9df734..1da1b6f7 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1679918112-3434798d3db2ecd76dfc1e78dbe255f6fe038068.profdata
+chrome-win32-main-1679929060-34f29b821ee8e423dbbb3d45b09b0227d8306c7d.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index d8b6613..30c09f5 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1679918112-4e49f4a193f7aef5137ab616e3255df467ae85da.profdata
+chrome-win64-main-1679929060-4c299167367e97d7cb321c6c0cf68fb0c992c785.profdata
diff --git a/chrome/chrome_cleaner/os/file_path_sanitization_unittest.cc b/chrome/chrome_cleaner/os/file_path_sanitization_unittest.cc
index ce0c69f..71c2f9c 100644
--- a/chrome/chrome_cleaner/os/file_path_sanitization_unittest.cc
+++ b/chrome/chrome_cleaner/os/file_path_sanitization_unittest.cc
@@ -21,14 +21,12 @@
 
 TEST(FilePathSanitizationTests, NormalizePath) {
 #if defined(ARCH_CPU_ARM64)
-  // on Win Arm64, ::GetLongPathName fails on c:\program files.
-  base::FilePath expected_path =
-      base::FilePath(L"c:\\program files (arm)\\desktop.ini");
-  EXPECT_EQ(NormalizePath(base::FilePath(L"C:\\PROGRA~2\\DESKTOP.INI")),
-            expected_path);
-  EXPECT_EQ(
-      NormalizePath(base::FilePath(L"c:\\pRoGrAm FiLeS (Arm)\\desktop.INI")),
-      expected_path);
+  // desktop.ini is not in C:\Program Files on ARM64 devices and the directory
+  // where it does exist (C:\Program Files (Arm)) doesn't have an 8.3 name.
+  // So, this is the best normalization test we can do.
+  base::FilePath expected_path = base::FilePath(L"c:\\program files");
+  EXPECT_EQ(NormalizePath(base::FilePath(L"C:\\PROGRA~1")), expected_path);
+  EXPECT_EQ(NormalizePath(base::FilePath(L"c:\\pRoGrAm FiLeS")), expected_path);
 #else
   base::FilePath expected_path =
       base::FilePath(L"c:\\program files\\desktop.ini");
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 84da6451..69782d1 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -269,7 +269,6 @@
         "$root_gen_dir/ash/webui/ash_os_feedback_untrusted_resources.pak",
         "$root_gen_dir/ash/webui/ash_personalization_app_resources.pak",
         "$root_gen_dir/ash/webui/ash_print_management_resources.pak",
-        "$root_gen_dir/ash/webui/ash_projector_annotator_trusted_resources.pak",
         "$root_gen_dir/ash/webui/ash_projector_annotator_untrusted_resources.pak",
         "$root_gen_dir/ash/webui/ash_projector_app_trusted_resources.pak",
         "$root_gen_dir/ash/webui/ash_projector_app_untrusted_resources.pak",
@@ -352,7 +351,6 @@
         "//ash/webui/resources:multidevice_debug_resources",
         "//ash/webui/resources:os_feedback_resources",
         "//ash/webui/resources:os_feedback_untrusted_resources",
-        "//ash/webui/resources:projector_annotator_trusted_resources",
         "//ash/webui/resources:projector_annotator_untrusted_resources",
         "//ash/webui/resources:projector_app_bundle_resources",
         "//ash/webui/resources:projector_app_trusted_resources",
diff --git a/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc b/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
index 78c941a..51fac952 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
@@ -770,7 +770,6 @@
     : public testing::TestWithParam<std::pair<std::string, bool>> {
  public:
   void SetUp() override {
-    BrowserInfo browser_info;
     std::unique_ptr<FakeDevToolsClient> client_uptr =
         std::make_unique<FakeDevToolsClient>("root");
     client_ptr = client_uptr.get();
@@ -783,14 +782,15 @@
   }
 
   void TearDown() override {
-    view.reset();
     client_ptr = nullptr;
+    view.reset();
   }
 
   bool IsW3C() { return GetParam().second; }
 
   std::string ElementKey() { return GetParam().first; }
 
+  BrowserInfo browser_info;
   std::unique_ptr<WebViewImpl> view;
   base::raw_ptr<FakeDevToolsClient> client_ptr;
 };
diff --git a/chrome/test/data/browsing_topics/page_with_custom_attribute_iframe.html b/chrome/test/data/browsing_topics/page_with_custom_attribute_iframe.html
new file mode 100644
index 0000000..faffadb5
--- /dev/null
+++ b/chrome/test/data/browsing_topics/page_with_custom_attribute_iframe.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <iframe {{MAYBE_BROWSING_TOPICS_ATTRIBUTE}} src="{{SRC_URL}}"></iframe>
+</body>
+</html>
diff --git a/chrome/test/data/extensions/api_test/stubs_app/background.js b/chrome/test/data/extensions/api_test/stubs_app/background.js
index a4f7b6c..e9536c6 100644
--- a/chrome/test/data/extensions/api_test/stubs_app/background.js
+++ b/chrome/test/data/extensions/api_test/stubs_app/background.js
@@ -24,12 +24,16 @@
         return;
       // Pieces of the module don't inherit from Array/Object.
       Array.prototype.forEach.call(section, function(entry) {
-        // Skip idle.getAutoLockDelay() and power.reportActivity() since they
-        // are restricted to certain platforms.
+        let fullName = `${namespace}.${entry.name}`;
+        // Skip a few functions:
+        // - `idle.getAutoLockDelay()` (restricted to certain platforms)
+        // - `power.reportActivity()` (restricted to certain platforms)
+        // - `runtime.getContexts()` (restricted to MV3)
         // TODO(https://crbug.com/921466)
-        if (entry.name != 'getAutoLockDelay' &&
-            entry.name != 'reportActivity') {
-          apiPaths.push(namespace + "." + entry.name);
+        const skipPaths = ['idle.getAutoLockDelay', 'power.reportActivity',
+                           'runtime.getContexts'];
+        if (!skipPaths.includes(fullName)) {
+          apiPaths.push(fullName);
         }
       });
     });
diff --git a/chrome/test/data/updater/CountingMetrics.plist b/chrome/test/data/updater/CountingMetrics.plist
new file mode 100644
index 0000000..d378bee6
--- /dev/null
+++ b/chrome/test/data/updater/CountingMetrics.plist
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>com.chromium.popularapp</key>
+	<dict>
+		<key>DayOfLastActive</key>
+		<integer>5921</integer>
+		<key>DayOfLastRollcall</key>
+		<integer>5922</integer>
+		<key>LastActivePingDate</key>
+		<date>2023-03-20T19:43:31Z</date>
+		<key>PingFreshness</key>
+		<string>53838040-FB56-40F8-8B50-244CEC1ADB40</string>
+		<key>ProductState</key>
+		<integer>1</integer>
+	</dict>
+	<key>om.chromium.kipple</key>
+	<dict>
+		<key>DayOfLastRollcall</key>
+		<string>5912</string>
+		<key>LastActivePingDate</key>
+		<date>2023-03-20T19:43:31Z</date>
+		<key>PingFreshness</key>
+		<string>CAC43E38-89E0-430F-92FB-0CA5B899B3C5</string>
+		<key>ProductState</key>
+		<integer>1</integer>
+	</dict>
+	<key>com.chromium.corruptedapp</key>
+	<dict>
+		<key>DayOfLastActive</key>
+		<integer>50001</integer>
+		<key>DayOfLastRollcall</key>
+		<integer>2999</integer>
+		<key>LastActivePingDate</key>
+		<date>2023-03-20T19:43:31Z</date>
+		<key>PingFreshness</key>
+		<string>CAC43E39-89E0-430F-92FB-0CA5B899B3C5</string>
+		<key>ProductState</key>
+		<integer>1</integer>
+	</dict>
+	<key>com.no.such.app</key>
+	<dict>
+		<key>DayOfLastActive</key>
+		<integer>5922</integer>
+		<key>DayOfLastRollcall</key>
+		<integer>5923</integer>
+		<key>LastActivePingDate</key>
+		<date>2023-03-20T19:43:31Z</date>
+		<key>PingFreshness</key>
+		<string>CAC43E48-89E0-430F-92FB-0CA5B899B3C5</string>
+		<key>ProductState</key>
+		<integer>1</integer>
+	</dict>
+</dict>
+</plist>
diff --git a/chrome/test/data/updater/Keystone.legacy.ticketstore b/chrome/test/data/updater/Keystone.legacy.ticketstore
index 40788477..515d0b55 100644
--- a/chrome/test/data/updater/Keystone.legacy.ticketstore
+++ b/chrome/test/data/updater/Keystone.legacy.ticketstore
Binary files differ
diff --git a/chrome/test/data/webui/chromeos/shortcut_customization/search_result_row_test.ts b/chrome/test/data/webui/chromeos/shortcut_customization/search_result_row_test.ts
index 22f25544..1210fb4 100644
--- a/chrome/test/data/webui/chromeos/shortcut_customization/search_result_row_test.ts
+++ b/chrome/test/data/webui/chromeos/shortcut_customization/search_result_row_test.ts
@@ -4,10 +4,13 @@
 import 'chrome://shortcut-customization/js/search/search_result_row.js';
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
+import {strictQuery} from 'chrome://resources/ash/common/typescript_utils/strict_query.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {SnapWindowLeftSearchResult} from 'chrome://shortcut-customization/js/fake_data.js';
+import {CycleTabsTextSearchResult, SnapWindowLeftSearchResult} from 'chrome://shortcut-customization/js/fake_data.js';
 import {SearchResultRowElement} from 'chrome://shortcut-customization/js/search/search_result_row.js';
+import {TextAcceleratorElement} from 'chrome://shortcut-customization/js/text_accelerator.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 function initSearchResultRowElement(): SearchResultRowElement {
   const element = document.createElement('search-result-row');
@@ -32,11 +35,11 @@
     assertTrue(!!searchResultRowElement);
   });
 
-  test('SearchResultTextDisplayed', async () => {
+  test('SearchResultDescriptionDisplayed', async () => {
     searchResultRowElement = initSearchResultRowElement();
     await flush();
     const searchResultTextElement =
-        searchResultRowElement!.shadowRoot!.querySelector('#searchResultText');
+        searchResultRowElement!.shadowRoot!.querySelector('#description');
     assertTrue(!!searchResultTextElement);
 
     searchResultRowElement.searchResult = SnapWindowLeftSearchResult;
@@ -44,4 +47,28 @@
     assertEquals(
         'Snap Window Left', searchResultTextElement.textContent?.trim());
   });
+
+  test('Text accelerators render correctly', async () => {
+    searchResultRowElement = initSearchResultRowElement();
+    searchResultRowElement.searchResult = CycleTabsTextSearchResult;
+    await flush();
+    assertEquals(
+        'Click or tap shelf icons 1-8',
+        strictQuery(
+            '#description', searchResultRowElement.shadowRoot, HTMLDivElement)
+            .innerText);
+
+    // Create a new TextAccelerator to compare against the one rendered by the
+    // SearchResultRow.
+    const textAccelElement = document.createElement('text-accelerator');
+    textAccelElement.parts = CycleTabsTextSearchResult.acceleratorInfos[0]
+                                 ?.layoutProperties.textAccelerator?.parts!;
+    document.body.appendChild(textAccelElement);
+    await flushTasks();
+    const searchResultRowTextAccelerator = strictQuery(
+        'text-accelerator', searchResultRowElement.shadowRoot,
+        TextAcceleratorElement);
+    assertEquals(
+        textAccelElement.innerHTML, searchResultRowTextAccelerator.innerHTML);
+  });
 });
diff --git a/chrome/test/data/webui/settings/autofill_page_test.ts b/chrome/test/data/webui/settings/autofill_page_test.ts
index 91aef55..b16b0131 100644
--- a/chrome/test/data/webui/settings/autofill_page_test.ts
+++ b/chrome/test/data/webui/settings/autofill_page_test.ts
@@ -7,7 +7,7 @@
 import {DomIf, flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {AutofillManagerImpl, PasswordsSectionElement, PasswordListItemElement, PaymentsManagerImpl, SettingsAutofillSectionElement, SettingsPaymentsSectionElement} from 'chrome://settings/lazy_load.js';
 import {buildRouter, Router, routes} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, OpenWindowProxyImpl, PasswordManagerImpl, SettingsAutofillPageElement, SettingsPluralStringProxyImpl, SettingsPrefsElement, SettingsRoutes} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, OpenWindowProxyImpl, PasswordManagerImpl, SettingsAutofillPageElement, SettingsPluralStringProxyImpl, SettingsPrefsElement, SettingsRoutes, PasswordManagerPage} from 'chrome://settings/settings.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestPluralStringProxy} from 'chrome://webui-test/test_plural_string_proxy.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
@@ -511,8 +511,8 @@
     assertTrue(autofillSection.$.passwordManagerButton.external);
 
     autofillSection.$.passwordManagerButton.click();
-    const url = await openWindowProxy.whenCalled('openUrl');
-    assertEquals(url, 'chrome://password-manager');
+    const param = await passwordManager.whenCalled('showPasswordManager');
+    assertEquals(PasswordManagerPage.PASSWORDS, param);
   });
 
   test('New Password Manager UI disabled', async function() {
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index c0899144..95d05c5 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -125,7 +125,6 @@
     "per_device_keyboard_test.js",
     "per_device_pointing_stick_subsection_test.js",
     "per_device_touchpad_subsection_test.js",
-    "per_device_touchpad_test.js",
     "personalization_page_with_personalization_hub_test.js",
     "privacy_hub_subpage_tests.js",
     "quick_unlock_authenticate_browsertest_chromeos.js",
@@ -207,6 +206,7 @@
     "device_page/per_device_mouse_subsection_test.ts",
     "device_page/per_device_mouse_test.ts",
     "device_page/per_device_pointing_stick_test.ts",
+    "device_page/per_device_touchpad_test.ts",
 
     "internet_page/tether_connection_dialog_test.ts",
 
diff --git a/chrome/test/data/webui/settings/chromeos/device_page/per_device_touchpad_test.ts b/chrome/test/data/webui/settings/chromeos/device_page/per_device_touchpad_test.ts
new file mode 100644
index 0000000..c523affd
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/device_page/per_device_touchpad_test.ts
@@ -0,0 +1,52 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {fakeTouchpads, fakeTouchpads2, SettingsPerDeviceTouchpadElement} from 'chrome://os-settings/chromeos/os_settings.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+
+suite('<settings-per-device-touchpad>', () => {
+  let perDeviceTouchpadPage: SettingsPerDeviceTouchpadElement;
+
+  setup(async () => {
+    perDeviceTouchpadPage =
+        document.createElement('settings-per-device-touchpad');
+    perDeviceTouchpadPage.set('touchpads', fakeTouchpads);
+    document.body.appendChild(perDeviceTouchpadPage);
+    await flushTasks();
+  });
+
+  teardown(() => {
+    perDeviceTouchpadPage.remove();
+  });
+
+  test('Touchpad page updates with new touchpads', async () => {
+    let subsections = perDeviceTouchpadPage.shadowRoot!.querySelectorAll(
+        'settings-per-device-touchpad-subsection');
+    assertEquals(fakeTouchpads.length, subsections.length);
+
+    // Check the number of subsections when the touchpad list is updated.
+    perDeviceTouchpadPage.set('touchpads', fakeTouchpads2);
+    await flushTasks();
+    subsections = perDeviceTouchpadPage.shadowRoot!.querySelectorAll(
+        'settings-per-device-touchpad-subsection');
+    assertEquals(fakeTouchpads2.length, subsections.length);
+  });
+
+  test(
+      'Display correct name used for internal/external touchpads', async () => {
+        const subsections = perDeviceTouchpadPage.shadowRoot!.querySelectorAll(
+            'settings-per-device-touchpad-subsection');
+        for (let i = 0; i < subsections.length; i++) {
+          const name =
+              subsections[i]!.shadowRoot!.querySelector('h2')!.textContent;
+          if (fakeTouchpads[i]!.isExternal) {
+            assertEquals(fakeTouchpads[i]!.name, name);
+          } else {
+            assertTrue(subsections[i]!.i18nExists('builtInTouchpadName'));
+            assertEquals('Built-in Touchpad', name);
+          }
+        }
+      });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 7304a94..3342b79 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -280,6 +280,11 @@
    {enabled: ['ash::features::kInputDeviceSettingsSplit']}
  ],
  [
+   'DevicePagePerDeviceTouchpad',
+   'device_page/per_device_touchpad_test.js',
+   {enabled: ['ash::features::kInputDeviceSettingsSplit']},
+ ],
+ [
    'DisplayAndMagnificationPage',
    'display_and_magnification_page_tests.js',
  ],
@@ -484,11 +489,6 @@
    {enabled: ['ash::features::kInputDeviceSettingsSplit']}
  ],
  [
-   'PerDeviceTouchpad',
-   'per_device_touchpad_test.js',
-   {enabled: ['ash::features::kInputDeviceSettingsSplit']},
- ],
- [
    'PerDeviceTouchpadSubsection', 'per_device_touchpad_subsection_test.js',
    {enabled: ['ash::features::kInputDeviceSettingsSplit']}
  ],
diff --git a/chrome/test/data/webui/settings/chromeos/per_device_touchpad_test.js b/chrome/test/data/webui/settings/chromeos/per_device_touchpad_test.js
deleted file mode 100644
index 3142373..0000000
--- a/chrome/test/data/webui/settings/chromeos/per_device_touchpad_test.js
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {fakeTouchpads, fakeTouchpads2, SettingsPerDeviceTouchpadElement} from 'chrome://os-settings/chromeos/os_settings.js';
-import {assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-
-suite('PerDeviceTouchpad', function() {
-  /**
-   * @type {?SettingsPerDeviceTouchpadElement}
-   */
-  let perDeviceTouchpadPage = null;
-
-  setup(() => {
-    PolymerTest.clearBody();
-  });
-
-  teardown(() => {
-    perDeviceTouchpadPage = null;
-  });
-
-  function initializePerDeviceTouchpadPage(touchpads = fakeTouchpads) {
-    perDeviceTouchpadPage =
-        document.createElement('settings-per-device-touchpad');
-    assertTrue(perDeviceTouchpadPage != null);
-    perDeviceTouchpadPage.touchpads = touchpads;
-    document.body.appendChild(perDeviceTouchpadPage);
-    return flushTasks();
-  }
-
-  test('Touchpad page updates with new touchpads', async () => {
-    await initializePerDeviceTouchpadPage();
-    let subsections = perDeviceTouchpadPage.shadowRoot.querySelectorAll(
-        'settings-per-device-touchpad-subsection');
-    assertEquals(fakeTouchpads.length, subsections.length);
-
-    // Check the number of subsections when the touchpad list is updated.
-    perDeviceTouchpadPage.touchpads = fakeTouchpads2;
-    await flushTasks();
-    subsections = perDeviceTouchpadPage.shadowRoot.querySelectorAll(
-        'settings-per-device-touchpad-subsection');
-    assertEquals(fakeTouchpads2.length, subsections.length);
-  });
-
-  test(
-      'Display correct name used for internal/external touchpads', async () => {
-        await initializePerDeviceTouchpadPage();
-        const subsections = perDeviceTouchpadPage.shadowRoot.querySelectorAll(
-            'settings-per-device-touchpad-subsection');
-        for (let i = 0; i < subsections.length; i++) {
-          const name =
-              subsections[i].shadowRoot.querySelector('h2').textContent;
-          if (fakeTouchpads[i].isExternal) {
-            assertEquals(fakeTouchpads[i].name, name);
-          } else {
-            assertTrue(subsections[i].i18nExists('builtInTouchpadName'));
-            assertEquals('Built-in Touchpad', name);
-          }
-        }
-      });
-});
diff --git a/chrome/test/data/webui/settings/test_password_manager_proxy.ts b/chrome/test/data/webui/settings/test_password_manager_proxy.ts
index 171ce12b..f66fedb 100644
--- a/chrome/test/data/webui/settings/test_password_manager_proxy.ts
+++ b/chrome/test/data/webui/settings/test_password_manager_proxy.ts
@@ -5,7 +5,7 @@
 /** @fileoverview Test implementation of PasswordManagerProxy. */
 
 // clang-format off
-import {AccountStorageOptInStateChangedListener, CredentialsChangedListener, HatsBrowserProxyImpl, PasswordCheckInteraction, PasswordCheckReferrer, PasswordCheckStatusChangedListener, PasswordExceptionListChangedListener, PasswordManagerProxy, PasswordsFileExportProgressListener, PasswordManagerAuthTimeoutListener, SavedPasswordListChangedListener, TrustSafetyInteraction} from 'chrome://settings/settings.js';
+import {AccountStorageOptInStateChangedListener, CredentialsChangedListener, HatsBrowserProxyImpl, PasswordCheckInteraction, PasswordCheckReferrer, PasswordCheckStatusChangedListener, PasswordExceptionListChangedListener, PasswordManagerProxy, PasswordsFileExportProgressListener, PasswordManagerAuthTimeoutListener, PasswordManagerPage, SavedPasswordListChangedListener, TrustSafetyInteraction} from 'chrome://settings/settings.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
@@ -116,6 +116,7 @@
       'recordChangePasswordFlowStarted',
       'recordPasswordCheckInteraction',
       'recordPasswordCheckReferrer',
+      'showPasswordManager',
       'removeException',
       'removeSavedPassword',
       'requestExportProgressStatus',
@@ -444,5 +445,9 @@
     this.methodCalled('switchBiometricAuthBeforeFillingState');
   }
 
+  showPasswordManager(page: PasswordManagerPage) {
+    this.methodCalled('showPasswordManager', page);
+  }
+
   undoRemoveSavedPasswordOrException() {}
 }
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index d5fa1a8..3caa0307 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -824,6 +824,7 @@
       sources += [
         "mac/keystone/ksadmin_unittest.cc",
         "mac/privileged_helper/privileged_helper_unittests.mm",
+        "mac/setup/keystone_unittest.cc",
         "mac/setup/ks_tickets_unittest.mm",
         "mac/setup/setup_unittest.mm",
         "policy/mac/managed_preference_policy_manager_impl_unittest.mm",
@@ -841,6 +842,7 @@
       ]
 
       data += [
+        "//chrome/test/data/updater/CountingMetrics.plist",
         "//chrome/test/data/updater/Keystone.ticketstore",
         "//chrome/test/data/updater/Keystone.withlaunchservicechecker.ticketstore",
         "//chrome/test/data/updater/Keystone.legacy.ticketstore",
diff --git a/chrome/updater/app/server/posix/app_server_posix_mac.cc b/chrome/updater/app/server/posix/app_server_posix_mac.cc
index 713915e4..5f445ad 100644
--- a/chrome/updater/app/server/posix/app_server_posix_mac.cc
+++ b/chrome/updater/app/server/posix/app_server_posix_mac.cc
@@ -8,6 +8,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "chrome/updater/mac/setup/keystone.h"
 #include "chrome/updater/registration_data.h"
+#include "chrome/updater/util/mac_util.h"
 
 namespace updater {
 
@@ -15,7 +16,8 @@
     base::RepeatingCallback<void(const RegistrationRequest&)>
         register_callback) {
   // TODO(crbug.com/1250524): This must not run concurrently with Keystone.
-  MigrateKeystoneTickets(updater_scope(), register_callback);
+  MigrateKeystoneApps(GetKeystoneFolderPath(updater_scope()).value(),
+                      register_callback);
 
   return true;
 }
diff --git a/chrome/updater/mac/setup/keystone.h b/chrome/updater/mac/setup/keystone.h
index 8c74044..13b8f046 100644
--- a/chrome/updater/mac/setup/keystone.h
+++ b/chrome/updater/mac/setup/keystone.h
@@ -5,12 +5,13 @@
 #ifndef CHROME_UPDATER_MAC_SETUP_KEYSTONE_H_
 #define CHROME_UPDATER_MAC_SETUP_KEYSTONE_H_
 
-#include <string>
-#include <vector>
-
 #include "base/functional/callback_forward.h"
 #include "chrome/updater/updater_scope.h"
 
+namespace base {
+class FilePath;
+}  // namespace base
+
 namespace updater {
 
 struct RegistrationRequest;
@@ -24,18 +25,11 @@
 
 // `Calls register_callback` with data from Keystone's ticket store if needed.
 // This is a best-effort operation, tickets with errors are not migrated.
-void MigrateKeystoneTickets(
-    UpdaterScope scope,
+void MigrateKeystoneApps(
+    const base::FilePath& keystone_path,
     base::RepeatingCallback<void(const RegistrationRequest&)>
         register_callback);
 
-namespace internal {
-
-std::vector<RegistrationRequest> TicketsToMigrate(
-    const std::string& ksadmin_tickets);
-
-}  // namespace internal
-
 }  // namespace updater
 
 #endif  // CHROME_UPDATER_MAC_SETUP_KEYSTONE_H_
diff --git a/chrome/updater/mac/setup/keystone.mm b/chrome/updater/mac/setup/keystone.mm
index 89bb729..17746d35 100644
--- a/chrome/updater/mac/setup/keystone.mm
+++ b/chrome/updater/mac/setup/keystone.mm
@@ -15,6 +15,7 @@
 #include "base/logging.h"
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
+#include "base/mac/scoped_nsobject.h"
 #include "base/process/launch.h"
 #include "base/process/process.h"
 #include "base/strings/string_split.h"
@@ -30,6 +31,81 @@
 #include "chrome/updater/util/util.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+// Class to read the Keystone apps' client-regulated-counting data.
+@interface CountingMetricsStore : NSObject {
+  base::scoped_nsobject<NSDictionary<NSString*, NSDictionary<NSString*, id>*>>
+      _metrics;
+}
+
++ (instancetype)storeAtPath:(const base::FilePath&)path;
+
+- (absl::optional<int>)dateLastActiveForApp:(NSString*)appid;
+- (absl::optional<int>)dateLastRollcallForApp:(NSString*)appid;
+
+@end
+
+@implementation CountingMetricsStore
+
++ (instancetype)storeAtPath:(const base::FilePath&)path {
+  return [[[CountingMetricsStore alloc]
+      initWithURL:[base::mac::FilePathToNSURL(path)
+                      URLByAppendingPathComponent:@"CountingMetrics.plist"]]
+      autorelease];
+}
+
+- (instancetype)initWithURL:(NSURL*)url {
+  if ((self = [super init])) {
+    NSError* error = nil;
+    _metrics.reset([[NSDictionary alloc] initWithContentsOfURL:url
+                                                         error:&error]);
+
+    if (error) {
+      LOG(WARNING) << "Failed to read client-regulated-counting data.";
+      [self release];
+      return nil;
+    }
+  }
+  return self;
+}
+
+- (absl::optional<int>)daynumValueOfKey:(NSString*)key forApp:(NSString*)appid {
+  id appObject = [_metrics objectForKey:appid.lowercaseString];
+  if (![appObject isKindOfClass:[NSDictionary class]]) {
+    LOG(WARNING) << "Malformed input client-regulated-counting data.";
+    return absl::nullopt;
+  }
+
+  id daynumObject = appObject[key];
+  if (!daynumObject) {
+    return absl::nullopt;
+  }
+
+  if (![daynumObject isKindOfClass:[NSNumber class]]) {
+    LOG(WARNING) << "daynum is not a number.";
+    return absl::nullopt;
+  }
+
+  // daynum the number of days since January 1, 2007. The accepted range is
+  // between 3000 (maps to Mar 20, 2015) and 50000 (maps to Nov 24, 2143).
+  int daynum = [daynumObject intValue];
+  if (daynum < 3000 || daynum > 50000) {
+    LOG(WARNING) << "Ignored out-of-range daynum: " << daynum;
+    return absl::nullopt;
+  }
+
+  return daynum;
+}
+
+- (absl::optional<int>)dateLastActiveForApp:(NSString*)appid {
+  return [self daynumValueOfKey:@"DayOfLastActive" forApp:appid];
+}
+
+- (absl::optional<int>)dateLastRollcallForApp:(NSString*)appid {
+  return [self daynumValueOfKey:@"DayOfLastRollcall" forApp:appid];
+}
+
+@end
+
 namespace updater {
 
 namespace {
@@ -204,19 +280,22 @@
   }
 }
 
-void MigrateKeystoneTickets(
-    UpdaterScope scope,
+void MigrateKeystoneApps(
+    const base::FilePath& keystone_path,
     base::RepeatingCallback<void(const RegistrationRequest&)>
         register_callback) {
   @autoreleasepool {
     NSDictionary<NSString*, KSTicket*>* store = [KSTicketStore
         readStoreWithPath:base::SysUTF8ToNSString(
-                              GetKeystoneFolderPath(scope)
-                                  ->Append(FILE_PATH_LITERAL("TicketStore"))
+                              keystone_path
+                                  .Append(FILE_PATH_LITERAL("TicketStore"))
                                   .Append(
                                       FILE_PATH_LITERAL("Keystone.ticketstore"))
                                   .AsUTF8Unsafe())];
 
+    CountingMetricsStore* metrics_store =
+        [CountingMetricsStore storeAtPath:keystone_path];
+
     for (NSString* key in store) {
       KSTicket* ticket = [store objectForKey:key];
 
@@ -248,6 +327,10 @@
         continue;
       }
 
+      registration.dla = [metrics_store dateLastActiveForApp:ticket.productID];
+      registration.dlrc =
+          [metrics_store dateLastRollcallForApp:ticket.productID];
+
       register_callback.Run(registration);
     }
   }
diff --git a/chrome/updater/mac/setup/keystone_unittest.cc b/chrome/updater/mac/setup/keystone_unittest.cc
new file mode 100644
index 0000000..625f8ce
--- /dev/null
+++ b/chrome/updater/mac/setup/keystone_unittest.cc
@@ -0,0 +1,88 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/updater/mac/setup/keystone.h"
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "base/test/bind.h"
+#include "base/version.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/updater/registration_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace updater {
+
+class KeystoneTest : public testing::Test {
+ public:
+  ~KeystoneTest() override = default;
+
+  void SetUp() override {
+    ASSERT_TRUE(temp_keystone_dir_.CreateUniqueTempDir());
+
+    base::FilePath ticket_path =
+        temp_keystone_dir_.GetPath().AppendASCII("TicketStore");
+    ASSERT_TRUE(base::CreateDirectory(ticket_path));
+
+    base::FilePath test_data_path;
+    ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_path));
+    test_data_path = test_data_path.AppendASCII("updater");
+
+    ASSERT_TRUE(base::CopyFile(
+        test_data_path.AppendASCII("Keystone.legacy.ticketstore"),
+        ticket_path.AppendASCII("Keystone.ticketstore")));
+    ASSERT_TRUE(base::CopyFile(
+        test_data_path.AppendASCII("CountingMetrics.plist"),
+        temp_keystone_dir_.GetPath().AppendASCII("CountingMetrics.plist")));
+  }
+
+ protected:
+  base::ScopedTempDir temp_keystone_dir_;
+};
+
+TEST_F(KeystoneTest, MigrateKeystoneApps) {
+  std::vector<RegistrationRequest> registration_requests;
+  MigrateKeystoneApps(
+      temp_keystone_dir_.GetPath(),
+      base::BindLambdaForTesting(
+          [&registration_requests](const RegistrationRequest& request) {
+            registration_requests.push_back(request);
+          }));
+
+  EXPECT_EQ(registration_requests.size(), 3u);
+
+  EXPECT_EQ(registration_requests[0].app_id, "com.chromium.CorruptedApp");
+  EXPECT_TRUE(registration_requests[0].brand_code.empty());
+  EXPECT_TRUE(registration_requests[0].brand_path.empty());
+  EXPECT_EQ(registration_requests[0].ap, "canary");
+  EXPECT_EQ(registration_requests[0].version, base::Version("1.2.1"));
+  EXPECT_EQ(registration_requests[0].existence_checker_path,
+            base::FilePath("/"));
+  EXPECT_FALSE(registration_requests[0].dla);   // Value is too big.
+  EXPECT_FALSE(registration_requests[0].dlrc);  // Value is too small.
+
+  EXPECT_EQ(registration_requests[1].app_id, "com.chromium.PopularApp");
+  EXPECT_TRUE(registration_requests[1].brand_code.empty());
+  EXPECT_EQ(registration_requests[1].brand_path, base::FilePath("/"));
+  EXPECT_EQ(registration_requests[1].ap, "GOOG");
+  EXPECT_EQ(registration_requests[1].version,
+            base::Version("101.100.1000.9999"));
+  EXPECT_EQ(registration_requests[1].existence_checker_path,
+            base::FilePath("/"));
+  EXPECT_EQ(registration_requests[1].dla.value(), 5921);
+  EXPECT_EQ(registration_requests[1].dlrc.value(), 5922);
+
+  EXPECT_EQ(registration_requests[2].app_id, "com.chromium.kipple");
+  EXPECT_TRUE(registration_requests[2].brand_path.empty());
+  EXPECT_EQ(registration_requests[2].existence_checker_path,
+            base::FilePath("/"));
+  EXPECT_FALSE(registration_requests[2].dla);   // No data.
+  EXPECT_FALSE(registration_requests[2].dlrc);  // String value is ignored.
+}
+
+}  // namespace updater
diff --git a/chrome/updater/persisted_data.cc b/chrome/updater/persisted_data.cc
index 9dae89f..bc48822 100644
--- a/chrome/updater/persisted_data.cc
+++ b/chrome/updater/persisted_data.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/persisted_data.h"
 
+#include <string>
 #include <vector>
 
 #include "base/base64.h"
@@ -35,12 +36,14 @@
 
 namespace {
 
-constexpr char kPV[] = "pv";    // Key for storing product version.
-constexpr char kFP[] = "fp";    // Key for storing fingerprint.
-constexpr char kECP[] = "ecp";  // Key for storing existence checker path.
-constexpr char kBC[] = "bc";    // Key for storing brand code.
-constexpr char kBP[] = "bp";    // Key for storing brand path.
-constexpr char kAP[] = "ap";    // Key for storing ap.
+constexpr char kPV[] = "pv";      // Key for storing product version.
+constexpr char kFP[] = "fp";      // Key for storing fingerprint.
+constexpr char kECP[] = "ecp";    // Key for storing existence checker path.
+constexpr char kBC[] = "bc";      // Key for storing brand code.
+constexpr char kBP[] = "bp";      // Key for storing brand path.
+constexpr char kAP[] = "ap";      // Key for storing ap.
+constexpr char kDLA[] = "dla";    // Key for storing date-last-active.
+constexpr char kDLRC[] = "dlrc";  // Key for storing date-last-rollcall.
 
 constexpr char kHadApps[] = "had_apps";
 constexpr char kUsageStatsEnabledKey[] = "usage_stats_enabled";
@@ -145,6 +148,16 @@
 #endif
 }
 
+void PersistedData::SetDateLastActive(const std::string& id, int dla) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  SetInteger(id, kDLA, dla);
+}
+
+void PersistedData::SetDateLastRollcall(const std::string& id, int dlrc) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  SetInteger(id, kDLRC, dlrc);
+}
+
 void PersistedData::RegisterApp(const RegistrationRequest& rq) {
   VLOG(2) << __func__ << ": Registering " << rq.app_id << " at version "
           << rq.version;
@@ -163,6 +176,12 @@
   if (!rq.ap.empty()) {
     SetAP(rq.app_id, rq.ap);
   }
+  if (rq.dla) {
+    SetDateLastActive(rq.app_id, rq.dla.value());
+  }
+  if (rq.dlrc) {
+    SetDateLastRollcall(rq.app_id, rq.dlrc.value());
+  }
 }
 
 bool PersistedData::RemoveApp(const std::string& id) {
@@ -230,6 +249,18 @@
   return app;
 }
 
+void PersistedData::SetInteger(const std::string& id,
+                               const std::string& key,
+                               int value) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!pref_service_) {
+    return;
+  }
+  ScopedDictPrefUpdate update(pref_service_,
+                              update_client::kPersistedDataPreference);
+  GetOrCreateAppKey(id, update.Get())->Set(key, value);
+}
+
 void PersistedData::SetString(const std::string& id,
                               const std::string& key,
                               const std::string& value) {
diff --git a/chrome/updater/persisted_data.h b/chrome/updater/persisted_data.h
index 7f25c13..656362051 100644
--- a/chrome/updater/persisted_data.h
+++ b/chrome/updater/persisted_data.h
@@ -74,6 +74,11 @@
   std::string GetAP(const std::string& id) const;
   void SetAP(const std::string& id, const std::string& ap);
 
+  // These functions set the client-regulated-counting data for the specified
+  // id. The functions are for app migration only.
+  void SetDateLastActive(const std::string& id, int dla);
+  void SetDateLastRollcall(const std::string& id, int dlrc);
+
   // This function sets any non-empty field in the registration request object
   // into the persistent data store.
   void RegisterApp(const RegistrationRequest& rq);
@@ -129,6 +134,7 @@
   base::Value::Dict* GetOrCreateAppKey(const std::string& id,
                                        base::Value::Dict& root);
 
+  void SetInteger(const std::string& id, const std::string& key, int value);
   std::string GetString(const std::string& id, const std::string& key) const;
   void SetString(const std::string& id,
                  const std::string& key,
diff --git a/chrome/updater/registration_data.h b/chrome/updater/registration_data.h
index a659ceef..970bb544 100644
--- a/chrome/updater/registration_data.h
+++ b/chrome/updater/registration_data.h
@@ -10,6 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/version.h"
 #include "chrome/updater/constants.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -47,6 +48,12 @@
   // whether an app has been uninstalled via deletion. May be the empty
   // string; if so, the app is assumed to be installed unconditionally.
   base::FilePath existence_checker_path;
+
+  // Date-last-active. The value is the number of days since Jan 1, 2007.
+  absl::optional<int> dla;
+
+  // Date-last-rollcall. The value is the number of days since Jan 1, 2007.
+  absl::optional<int> dlrc;
 };
 
 }  // namespace updater
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index d1bb1ff..1343102d 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-15398.0.0
\ No newline at end of file
+15399.0.0
\ No newline at end of file
diff --git a/chromeos/ash/components/audio/cras_audio_handler.cc b/chromeos/ash/components/audio/cras_audio_handler.cc
index 2b1e511..b2f3781 100644
--- a/chromeos/ash/components/audio/cras_audio_handler.cc
+++ b/chromeos/ash/components/audio/cras_audio_handler.cc
@@ -18,6 +18,7 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/system/system_monitor.h"
@@ -860,6 +861,14 @@
     observer.OnOutputMuteChanged(output_mute_on_);
 }
 
+void CrasAudioHandler::SetOutputMute(
+    bool mute_on,
+    CrasAudioHandler::AudioSettingsChangeSource source) {
+  SetOutputMute(mute_on);
+  base::UmaHistogramEnumeration(
+      CrasAudioHandler::kOutputVolumeMuteSourceHistogramName, source);
+}
+
 void CrasAudioHandler::SetOutputMuteLockedBySecurityCurtain(bool mute_on) {
   if (output_mute_forced_by_security_curtain_ == mute_on)
     return;
@@ -904,6 +913,15 @@
   }
 }
 
+void CrasAudioHandler::SetInputMute(
+    bool mute_on,
+    InputMuteChangeMethod method,
+    CrasAudioHandler::AudioSettingsChangeSource source) {
+  SetInputMute(mute_on, method);
+  base::UmaHistogramEnumeration(
+      CrasAudioHandler::kInputGainMuteSourceHistogramName, source);
+}
+
 void CrasAudioHandler::SetActiveDevice(const AudioDevice& active_device,
                                        bool notify,
                                        DeviceActivateType activate_by) {
@@ -1006,6 +1024,20 @@
     audio_pref_handler_->SetMuteValue(*device, mute_on);
 }
 
+void CrasAudioHandler::SetMuteForDevice(
+    uint64_t device_id,
+    bool mute_on,
+    CrasAudioHandler::AudioSettingsChangeSource source) {
+  SetMuteForDevice(device_id, mute_on);
+  if (device_id == active_output_node_id_) {
+    base::UmaHistogramEnumeration(
+        CrasAudioHandler::kOutputVolumeMuteSourceHistogramName, source);
+  } else if (device_id == active_input_node_id_) {
+    base::UmaHistogramEnumeration(
+        CrasAudioHandler::kInputGainMuteSourceHistogramName, source);
+  }
+}
+
 // If the HDMI device is the active output device, when the device enters/exits
 // docking mode, or HDMI display changes resolution, or chromeos device
 // suspends/resumes, cras will lose the HDMI output node for a short period of
diff --git a/chromeos/ash/components/audio/cras_audio_handler.h b/chromeos/ash/components/audio/cras_audio_handler.h
index ad69c770..54d36503 100644
--- a/chromeos/ash/components/audio/cras_audio_handler.h
+++ b/chromeos/ash/components/audio/cras_audio_handler.h
@@ -97,8 +97,12 @@
       base::Seconds(2);
   static constexpr char kInputGainChangedSourceHistogramName[] =
       "Cras.InputGainChangedSource";
+  static constexpr char kInputGainMuteSourceHistogramName[] =
+      "Cras.InputGainMutedSource";
   static constexpr char kOutputVolumeChangedSourceHistogramName[] =
       "Cras.OutputVolumeChangedSource";
+  static constexpr char kOutputVolumeMuteSourceHistogramName[] =
+      "Cras.OutputVolumeMutedSource";
 
   class AudioObserver {
    public:
@@ -379,12 +383,23 @@
   // Mutes or unmutes audio output device.
   void SetOutputMute(bool mute_on);
 
+  // Mutes or unmutes audio output device including the `source` to record to
+  // metrics.
+  void SetOutputMute(bool mute_on,
+                     CrasAudioHandler::AudioSettingsChangeSource source);
+
   // Mutes or unmutes audio output device by security curtain
   void SetOutputMuteLockedBySecurityCurtain(bool mute_on);
 
   // Mutes or unmutes audio input device.
   void SetInputMute(bool mute_on, InputMuteChangeMethod method);
 
+  // Mutes or unmutes audio input device including the `source` to record to
+  // metrics.
+  void SetInputMute(bool mute_on,
+                    InputMuteChangeMethod method,
+                    CrasAudioHandler::AudioSettingsChangeSource source);
+
   // Switches active audio device to |device|. |activate_by| indicates why
   // the device is switched to active: by user's manual choice, by priority,
   // or by restoring to its previous active state.
@@ -398,6 +413,11 @@
   // Sets the mute for device.
   void SetMuteForDevice(uint64_t device_id, bool mute_on);
 
+  // Sets the mute for device including the `source` to record to metrics.
+  void SetMuteForDevice(uint64_t device_id,
+                        bool mute_on,
+                        CrasAudioHandler::AudioSettingsChangeSource source);
+
   // Activates or deactivates keyboard mic if there's one.
   void SetKeyboardMicActive(bool active);
 
diff --git a/chromeos/ash/components/audio/cras_audio_handler_unittest.cc b/chromeos/ash/components/audio/cras_audio_handler_unittest.cc
index fee4cf4..b526dc0 100644
--- a/chromeos/ash/components/audio/cras_audio_handler_unittest.cc
+++ b/chromeos/ash/components/audio/cras_audio_handler_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/run_loop.h"
 #include "base/system/system_monitor.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
 #include "chromeos/ash/components/audio/audio_devices_pref_handler.h"
@@ -569,6 +570,7 @@
   scoped_refptr<AudioDevicesPrefHandlerStub> audio_pref_handler_;
   std::unique_ptr<FakeMediaControllerManager> fake_manager_;
   std::unique_ptr<FakeVideoCaptureManager> video_capture_manager_;
+  base::HistogramTester histogram_tester_;
 };
 
 class HDMIRediscoverWaiter {
@@ -2173,6 +2175,25 @@
   EXPECT_FALSE(audio_pref_handler_->GetMuteValue(speaker));
 }
 
+TEST_P(CrasAudioHandlerTest, SetOutputMuteWithSource) {
+  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
+  SetUpCrasAudioHandler(audio_nodes);
+  histogram_tester_.ExpectBucketCount(
+      CrasAudioHandler::kOutputVolumeMuteSourceHistogramName,
+      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray,
+      /*expected_count=*/0);
+
+  // Mute the device.
+  cras_audio_handler_->SetOutputMute(
+      true, CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);
+
+  // Verify mute source is recorded.
+  histogram_tester_.ExpectBucketCount(
+      CrasAudioHandler::kOutputVolumeMuteSourceHistogramName,
+      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray,
+      /*expected_count=*/1);
+}
+
 TEST_P(CrasAudioHandlerTest, SetInputMute) {
   AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic});
   SetUpCrasAudioHandler(audio_nodes);
@@ -2195,6 +2216,26 @@
   EXPECT_EQ(2, test_observer_->input_mute_changed_count());
 }
 
+TEST_P(CrasAudioHandlerTest, SetInputMuteWithSource) {
+  AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalMic});
+  SetUpCrasAudioHandler(audio_nodes);
+  histogram_tester_.ExpectBucketCount(
+      CrasAudioHandler::kInputGainMuteSourceHistogramName,
+      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray,
+      /*expected_count=*/0);
+
+  // Mute the device.
+  cras_audio_handler_->SetInputMute(
+      true, CrasAudioHandler::InputMuteChangeMethod::kOther,
+      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray);
+
+  // Verify mute source is recorded.
+  histogram_tester_.ExpectBucketCount(
+      CrasAudioHandler::kInputGainMuteSourceHistogramName,
+      CrasAudioHandler::AudioSettingsChangeSource::kSystemTray,
+      /*expected_count=*/1);
+}
+
 TEST_P(CrasAudioHandlerTest, SetOutputVolumePercent) {
   AudioNodeList audio_nodes = GenerateAudioNodeList({kInternalSpeaker});
   SetUpCrasAudioHandler(audio_nodes);
diff --git a/chromeos/ash/components/audio/cros_audio_config_impl.cc b/chromeos/ash/components/audio/cros_audio_config_impl.cc
index 9374296..4f5756e8 100644
--- a/chromeos/ash/components/audio/cros_audio_config_impl.cc
+++ b/chromeos/ash/components/audio/cros_audio_config_impl.cc
@@ -241,7 +241,8 @@
     return;
   }
 
-  audio_handler->SetOutputMute(muted);
+  audio_handler->SetOutputMute(
+      muted, CrasAudioHandler::AudioSettingsChangeSource::kOsSettings);
   RecordMuteStateChanged(kOutputMuteChangeHistogramName, muted);
 }
 
@@ -316,8 +317,9 @@
     return;
   }
 
-  audio_handler->SetMuteForDevice(audio_handler->GetPrimaryActiveInputNode(),
-                                  muted);
+  audio_handler->SetMuteForDevice(
+      audio_handler->GetPrimaryActiveInputNode(), muted,
+      CrasAudioHandler::AudioSettingsChangeSource::kOsSettings);
   RecordMuteStateChanged(kInputMuteChangeHistogramName, muted);
 }
 
diff --git a/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc b/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc
index a37e0ae..c91d042 100644
--- a/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc
+++ b/chromeos/ash/components/audio/cros_audio_config_impl_unittest.cc
@@ -883,6 +883,9 @@
                                       AudioMuteButtonAction::kMuted, 1);
   histogram_tester_.ExpectBucketCount(kOutputMuteChangeHistogramName,
                                       AudioMuteButtonAction::kUnmuted, 1);
+  histogram_tester_.ExpectBucketCount(
+      CrasAudioHandler::kOutputVolumeMuteSourceHistogramName,
+      CrasAudioHandler::AudioSettingsChangeSource::kOsSettings, 2);
 }
 
 TEST_F(CrosAudioConfigImplTest, SetInputMuted) {
@@ -909,6 +912,9 @@
                                       AudioMuteButtonAction::kMuted, 1);
   histogram_tester_.ExpectBucketCount(kInputMuteChangeHistogramName,
                                       AudioMuteButtonAction::kUnmuted, 1);
+  histogram_tester_.ExpectBucketCount(
+      CrasAudioHandler::kInputGainMuteSourceHistogramName,
+      CrasAudioHandler::AudioSettingsChangeSource::kOsSettings, 2);
 
   // Simulate turning physical switch on.
   SetInputMuteState(mojom::MuteState::kMutedExternally, /*switch_on=*/true);
@@ -931,6 +937,9 @@
                                       AudioMuteButtonAction::kMuted, 1);
   histogram_tester_.ExpectBucketCount(kInputMuteChangeHistogramName,
                                       AudioMuteButtonAction::kUnmuted, 1);
+  histogram_tester_.ExpectBucketCount(
+      CrasAudioHandler::kInputGainMuteSourceHistogramName,
+      CrasAudioHandler::AudioSettingsChangeSource::kOsSettings, 2);
 }
 
 // Verify merging front and rear mic into a single device returns expected
diff --git a/chromeos/ash/components/dbus/featured/BUILD.gn b/chromeos/ash/components/dbus/featured/BUILD.gn
index e07a157..5061483 100644
--- a/chromeos/ash/components/dbus/featured/BUILD.gn
+++ b/chromeos/ash/components/dbus/featured/BUILD.gn
@@ -11,8 +11,8 @@
   defines = [ "IS_CHROMEOS_ASH_COMPONENTS_DBUS_FEATURED_IMPL" ]
 
   deps = [
+    ":proto",
     "//base",
-    "//components/variations/proto:cros_safe_seed_proto",
     "//dbus",
   ]
 
@@ -29,9 +29,9 @@
 
   deps = [
     ":featured",
+    ":proto",
     "//base",
     "//base/test:test_support",
-    "//components/variations/proto:cros_safe_seed_proto",
     "//dbus",
     "//dbus:test_support",
     "//testing/gmock",
@@ -40,3 +40,9 @@
 
   sources = [ "featured_client_unittest.cc" ]
 }
+
+proto_library("proto") {
+  sources = [ "//third_party/cros_system_api/dbus/featured/featured.proto" ]
+
+  proto_out_dir = "chromeos/ash/components/dbus/featured"
+}
diff --git a/chromeos/ash/components/dbus/featured/DEPS b/chromeos/ash/components/dbus/featured/DEPS
deleted file mode 100644
index ab865764..0000000
--- a/chromeos/ash/components/dbus/featured/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "+components/variations/proto/cros_safe_seed.pb.h",
-]
diff --git a/chromeos/ash/components/dbus/featured/fake_featured_client.cc b/chromeos/ash/components/dbus/featured/fake_featured_client.cc
index bd5a2d9..73e233ca 100644
--- a/chromeos/ash/components/dbus/featured/fake_featured_client.cc
+++ b/chromeos/ash/components/dbus/featured/fake_featured_client.cc
@@ -8,7 +8,7 @@
 
 #include "base/check_op.h"
 #include "base/functional/callback.h"
-#include "components/variations/proto/cros_safe_seed.pb.h"
+#include "chromeos/ash/components/dbus/featured/featured.pb.h"
 #include "dbus/object_proxy.h"
 
 namespace ash::featured {
@@ -40,7 +40,7 @@
 }
 
 void FakeFeaturedClient::HandleSeedFetched(
-    const variations::SeedDetails& safe_seed,
+    const ::featured::SeedDetails& safe_seed,
     base::OnceCallback<void(bool success)> callback) {
   std::move(callback).Run(success_);
 }
diff --git a/chromeos/ash/components/dbus/featured/fake_featured_client.h b/chromeos/ash/components/dbus/featured/fake_featured_client.h
index 4182c10..2f204470 100644
--- a/chromeos/ash/components/dbus/featured/fake_featured_client.h
+++ b/chromeos/ash/components/dbus/featured/fake_featured_client.h
@@ -7,8 +7,8 @@
 
 #include "base/component_export.h"
 #include "base/functional/callback.h"
+#include "chromeos/ash/components/dbus/featured/featured.pb.h"
 #include "chromeos/ash/components/dbus/featured/featured_client.h"
-#include "components/variations/proto/cros_safe_seed.pb.h"
 
 namespace ash::featured {
 
@@ -31,7 +31,7 @@
   // |callback| is run with true by default. Call |SetCallbackSuccess|
   // to change the callback parameter.
   void HandleSeedFetched(
-      const variations::SeedDetails& safe_seed,
+      const ::featured::SeedDetails& safe_seed,
       base::OnceCallback<void(bool success)> callback) override;
 
  private:
diff --git a/chromeos/ash/components/dbus/featured/featured_client.cc b/chromeos/ash/components/dbus/featured/featured_client.cc
index 47ab378..3e53148 100644
--- a/chromeos/ash/components/dbus/featured/featured_client.cc
+++ b/chromeos/ash/components/dbus/featured/featured_client.cc
@@ -9,7 +9,7 @@
 #include <base/logging.h>
 #include "base/functional/callback.h"
 #include "chromeos/ash/components/dbus/featured/fake_featured_client.h"
-#include "components/variations/proto/cros_safe_seed.pb.h"
+#include "chromeos/ash/components/dbus/featured/featured.pb.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
@@ -50,7 +50,7 @@
   }
 
   void HandleSeedFetched(
-      const variations::SeedDetails& safe_seed,
+      const ::featured::SeedDetails& safe_seed,
       base::OnceCallback<void(bool success)> callback) override {
     dbus::MethodCall method_call(::featured::kFeaturedInterface,
                                  "HandleSeedFetched");
diff --git a/chromeos/ash/components/dbus/featured/featured_client.h b/chromeos/ash/components/dbus/featured/featured_client.h
index fa91923..419500b 100644
--- a/chromeos/ash/components/dbus/featured/featured_client.h
+++ b/chromeos/ash/components/dbus/featured/featured_client.h
@@ -7,7 +7,7 @@
 
 #include "base/component_export.h"
 #include "base/functional/callback.h"
-#include "components/variations/proto/cros_safe_seed.pb.h"
+#include "chromeos/ash/components/dbus/featured/featured.pb.h"
 
 namespace dbus {
 class Bus;
@@ -37,7 +37,7 @@
 
   // Asynchronously calls featured's `HandleSeedFetched`.
   virtual void HandleSeedFetched(
-      const variations::SeedDetails& safe_seed,
+      const ::featured::SeedDetails& safe_seed,
       base::OnceCallback<void(bool success)> callback) = 0;
 
  protected:
diff --git a/chromeos/ash/components/dbus/featured/featured_client_unittest.cc b/chromeos/ash/components/dbus/featured/featured_client_unittest.cc
index 967e4fdf..783e74b 100644
--- a/chromeos/ash/components/dbus/featured/featured_client_unittest.cc
+++ b/chromeos/ash/components/dbus/featured/featured_client_unittest.cc
@@ -6,7 +6,7 @@
 #include "base/check_op.h"
 #include "base/test/bind.h"
 #include "chromeos/ash/components/dbus/featured/fake_featured_client.h"
-#include "components/variations/proto/cros_safe_seed.pb.h"
+#include "chromeos/ash/components/dbus/featured/featured.pb.h"
 #include "dbus/message.h"
 #include "dbus/mock_bus.h"
 #include "dbus/mock_object_proxy.h"
@@ -92,7 +92,7 @@
 
   ASSERT_NE(client, nullptr);
 
-  variations::SeedDetails safe_seed;
+  ::featured::SeedDetails safe_seed;
 
   bool ran_callback = false;
   client->HandleSeedFetched(
@@ -128,7 +128,7 @@
 
   ASSERT_NE(client, nullptr);
 
-  variations::SeedDetails safe_seed;
+  ::featured::SeedDetails safe_seed;
 
   bool ran_callback = false;
   client->HandleSeedFetched(
@@ -159,7 +159,7 @@
 
   ASSERT_NE(client, nullptr);
 
-  variations::SeedDetails safe_seed;
+  ::featured::SeedDetails safe_seed;
 
   bool ran_callback = false;
   client->HandleSeedFetched(
@@ -183,7 +183,7 @@
 
   ASSERT_NE(client, nullptr);
 
-  variations::SeedDetails safe_seed;
+  ::featured::SeedDetails safe_seed;
 
   bool ran_callback = false;
   client->HandleSeedFetched(
@@ -207,7 +207,7 @@
 
   ASSERT_NE(client, nullptr);
 
-  variations::SeedDetails safe_seed;
+  ::featured::SeedDetails safe_seed;
   client->SetCallbackSuccess(false);
 
   bool ran_callback = false;
diff --git a/chromeos/services/network_config/OWNERS b/chromeos/services/network_config/OWNERS
index 2abc848..ae7fad83 100644
--- a/chromeos/services/network_config/OWNERS
+++ b/chromeos/services/network_config/OWNERS
@@ -1,4 +1,3 @@
-azeemarshad@chromium.org
 chadduffin@chromium.org
 jiajunz@google.com
 khorimoto@chromium.org
diff --git a/components/app_restore/app_restore_data.cc b/components/app_restore/app_restore_data.cc
index 11814d84..98e03e49 100644
--- a/components/app_restore/app_restore_data.cc
+++ b/components/app_restore/app_restore_data.cc
@@ -32,6 +32,7 @@
 constexpr char kActivationIndexKey[] = "index";
 constexpr char kFirstNonPinnedTabIndexKey[] = "first_non_pinned_tab_index";
 constexpr char kDeskIdKey[] = "desk_id";
+constexpr char kDeskGuidKey[] = "desk_guid";
 constexpr char kCurrentBoundsKey[] = "current_bounds";
 constexpr char kWindowStateTypeKey[] = "window_state_type";
 constexpr char kPreMinimizedShowStateTypeKey[] = "pre_min_state";
@@ -219,6 +220,14 @@
              : absl::nullopt;
 }
 
+base::GUID GetGuidValueFromDict(const base::Value::Dict& dict,
+                                const std::string& key_name) {
+  if (const std::string* value = dict.FindString(key_name)) {
+    return base::GUID::ParseCaseInsensitive(*value);
+  }
+  return base::GUID();
+}
+
 }  // namespace
 
 AppRestoreData::AppRestoreData() = default;
@@ -239,6 +248,7 @@
   first_non_pinned_tab_index =
       GetIntValueFromDict(data, kFirstNonPinnedTabIndexKey);
   desk_id = GetIntValueFromDict(data, kDeskIdKey);
+  desk_guid = GetGuidValueFromDict(data, kDeskGuidKey);
   current_bounds = GetBoundsRectFromDict(data, kCurrentBoundsKey);
   window_state_type = GetWindowStateTypeFromDict(data);
   pre_minimized_show_state_type = GetPreMinimizedShowStateTypeFromDict(data);
@@ -330,6 +340,10 @@
   if (desk_id.has_value())
     data->desk_id = desk_id.value();
 
+  if (desk_guid.is_valid()) {
+    data->desk_guid = desk_guid;
+  }
+
   if (current_bounds.has_value())
     data->current_bounds = current_bounds.value();
 
@@ -427,6 +441,10 @@
   if (desk_id.has_value())
     launch_info_dict.Set(kDeskIdKey, desk_id.value());
 
+  if (desk_guid.is_valid()) {
+    launch_info_dict.Set(kDeskGuidKey, desk_guid.AsLowercaseString());
+  }
+
   if (current_bounds.has_value()) {
     launch_info_dict.Set(kCurrentBoundsKey,
                          ConvertRectToList(current_bounds.value()));
@@ -483,6 +501,10 @@
   if (window_info.desk_id.has_value())
     desk_id = window_info.desk_id.value();
 
+  if (window_info.desk_guid.is_valid()) {
+    desk_guid = window_info.desk_guid;
+  }
+
   if (window_info.current_bounds.has_value())
     current_bounds = window_info.current_bounds.value();
 
@@ -519,6 +541,7 @@
 void AppRestoreData::ClearWindowInfo() {
   activation_index.reset();
   desk_id.reset();
+  desk_guid = base::GUID();
   current_bounds.reset();
   window_state_type.reset();
   pre_minimized_show_state_type.reset();
@@ -562,6 +585,10 @@
   if (desk_id.has_value())
     window_info->desk_id = desk_id.value();
 
+  if (desk_guid.is_valid()) {
+    window_info->desk_guid = desk_guid;
+  }
+
   if (current_bounds.has_value())
     window_info->current_bounds = current_bounds.value();
 
@@ -635,7 +662,8 @@
          app_type_browser == other.app_type_browser &&
          app_name == other.app_name && title == other.title &&
          activation_index == other.activation_index &&
-         desk_id == other.desk_id && current_bounds == other.current_bounds &&
+         desk_id == other.desk_id && desk_guid == other.desk_guid &&
+         current_bounds == other.current_bounds &&
          window_state_type == other.window_state_type &&
          pre_minimized_show_state_type == other.pre_minimized_show_state_type &&
          snap_percentage == other.snap_percentage &&
diff --git a/components/app_restore/app_restore_data.h b/components/app_restore/app_restore_data.h
index e7684d5..6db8382 100644
--- a/components/app_restore/app_restore_data.h
+++ b/components/app_restore/app_restore_data.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/component_export.h"
+#include "base/guid.h"
 #include "base/values.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
@@ -116,6 +117,8 @@
   // in full restore are persistsed by sessions.  This field is not converted to
   // base::value in base value conversions.
   absl::optional<std::vector<tab_groups::TabGroupInfo>> tab_group_infos;
+  // The GUID of the desk that this window was on.
+  base::GUID desk_guid;
 
   // Extra ARC window's information.
   absl::optional<gfx::Size> minimum_size;
diff --git a/components/app_restore/full_restore_read_and_save_unittest.cc b/components/app_restore/full_restore_read_and_save_unittest.cc
index 3eb14e9..150c238 100644
--- a/components/app_restore/full_restore_read_and_save_unittest.cc
+++ b/components/app_restore/full_restore_read_and_save_unittest.cc
@@ -27,6 +27,7 @@
 #include "components/services/app_service/public/cpp/intent.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/aura_test_helper.h"
@@ -41,6 +42,9 @@
 
 namespace {
 
+using testing::ElementsAre;
+using testing::Pair;
+
 constexpr char kAppId[] = "aaa";
 
 constexpr int32_t kId1 = 100;
@@ -69,6 +73,10 @@
 
 constexpr uint32_t kBrowserSessionId = 56;
 
+// Randomly generated desk GUID to test saving removing desk GUID.
+const base::GUID kRemovingDeskGuid = base::GUID::GenerateRandomV4();
+const base::GUID kNonRemovingDeskGuid = base::GUID::GenerateRandomV4();
+
 }  // namespace
 
 class FullRestoreReadHandlerTestApi {
@@ -334,7 +342,8 @@
   std::unique_ptr<aura::Window> CreateWindowInfo(
       int32_t id,
       int32_t index,
-      ash::AppType app_type = ash::AppType::BROWSER) {
+      ash::AppType app_type = ash::AppType::BROWSER,
+      base::GUID desk_guid = base::GUID()) {
     std::unique_ptr<aura::Window> window(
         aura::test::CreateTestWindowWithId(id, nullptr));
     app_restore::WindowInfo window_info;
@@ -342,6 +351,7 @@
     window->SetProperty(aura::client::kAppType, static_cast<int>(app_type));
     window->SetProperty(app_restore::kWindowIdKey, id);
     window_info.activation_index = index;
+    window_info.desk_guid = desk_guid;
     full_restore::SaveWindowInfo(window_info);
     return window;
   }
@@ -1168,4 +1178,44 @@
   EXPECT_TRUE(restore_data->app_id_to_launch_list().empty());
 }
 
+// Verifies that saving a removing desk's GUID in `RestoreData` allows for us to
+// prevent the windows in that desk from being restored.
+TEST_F(FullRestoreReadAndSaveTest, PreventWindowsOnRemovingDeskFromRestoring) {
+  FullRestoreSaveHandler* save_handler = GetSaveHandler();
+  base::OneShotTimer* timer = save_handler->GetTimerForTesting();
+
+  // Add app launch info, and verify the timer starts.
+  AddAppLaunchInfo(GetPath(), kId1);
+  ASSERT_TRUE(timer->IsRunning());
+
+  // Add one more app launch info, and verify the timer is still running.
+  AddAppLaunchInfo(GetPath(), kId2);
+  ASSERT_TRUE(timer->IsRunning());
+
+  // Create two windows. Establish that `window1` will be on the removing desk
+  // and `window2` will be on the non-removing desk.
+  std::unique_ptr<aura::Window> window1 = CreateWindowInfo(
+      kId1, kActivationIndex1, ash::AppType::BROWSER, kRemovingDeskGuid);
+  std::unique_ptr<aura::Window> window2 = CreateWindowInfo(
+      kId2, kActivationIndex2, ash::AppType::BROWSER, kNonRemovingDeskGuid);
+
+  // Establish that the desk with `kRemovingDeskGuid` as its GUID is being
+  // removed.
+  save_handler->SaveRemovingDeskGuid(kRemovingDeskGuid);
+
+  // Simulate timeout, which should trigger a save, and verify the timer stops.
+  timer->FireNow();
+  task_environment().RunUntilIdle();
+
+  ReadFromFile(GetPath());
+
+  // Verify the restore data can be read correctly.
+  const auto* restore_data = GetRestoreData(GetPath());
+  ASSERT_TRUE(restore_data);
+
+  // The launch list in `restore_data` should only have `window2`.
+  EXPECT_THAT(restore_data->app_id_to_launch_list(),
+              ElementsAre(Pair(kAppId, ElementsAre(Pair(kId2, testing::_)))));
+}
+
 }  // namespace full_restore
diff --git a/components/app_restore/full_restore_save_handler.cc b/components/app_restore/full_restore_save_handler.cc
index 0b8aaf5..32f7624ae 100644
--- a/components/app_restore/full_restore_save_handler.cc
+++ b/components/app_restore/full_restore_save_handler.cc
@@ -326,6 +326,16 @@
   ModifyWindowInfo(window_id, window_info);
 }
 
+void FullRestoreSaveHandler::SaveRemovingDeskGuid(
+    const base::GUID& removing_desk_guid) {
+  profile_path_to_restore_data_[active_profile_path_].set_removing_desk_guid(
+      removing_desk_guid);
+
+  pending_save_profile_paths_.insert(active_profile_path_);
+
+  MaybeStartSaveTimer(active_profile_path_);
+}
+
 void FullRestoreSaveHandler::OnLacrosChromeAppWindowAdded(
     const std::string& app_id,
     const std::string& window_id) {
diff --git a/components/app_restore/full_restore_save_handler.h b/components/app_restore/full_restore_save_handler.h
index 4c7d677..0be949ce 100644
--- a/components/app_restore/full_restore_save_handler.h
+++ b/components/app_restore/full_restore_save_handler.h
@@ -13,6 +13,7 @@
 
 #include "base/component_export.h"
 #include "base/containers/flat_map.h"
+#include "base/guid.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_multi_source_observation.h"
@@ -110,6 +111,10 @@
   // Saves |window_info| to |profile_path_to_restore_data_|.
   void SaveWindowInfo(const app_restore::WindowInfo& window_info);
 
+  // Saves `removing_desk_guid` to the restore data for the currently active
+  // profile path.
+  void SaveRemovingDeskGuid(const base::GUID& removing_desk_guid);
+
   // Invoked when an Chrome app Lacros window is created. `app_id` is the
   // AppService id, and `window_id` is the wayland app_id property for the
   // window.
diff --git a/components/app_restore/full_restore_utils.cc b/components/app_restore/full_restore_utils.cc
index 9b0cc55..b5bf5117 100644
--- a/components/app_restore/full_restore_utils.cc
+++ b/components/app_restore/full_restore_utils.cc
@@ -75,4 +75,13 @@
       app_id, window_id);
 }
 
+void SaveRemovingDeskGuid(const base::GUID& removing_desk_guid) {
+  FullRestoreSaveHandler::GetInstance()->SaveRemovingDeskGuid(
+      removing_desk_guid);
+}
+
+void ResetRemovingDeskGuid() {
+  FullRestoreSaveHandler::GetInstance()->SaveRemovingDeskGuid(base::GUID());
+}
+
 }  // namespace full_restore
diff --git a/components/app_restore/full_restore_utils.h b/components/app_restore/full_restore_utils.h
index b4d5bc69..d27d662 100644
--- a/components/app_restore/full_restore_utils.h
+++ b/components/app_restore/full_restore_utils.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/component_export.h"
+#include "base/guid.h"
 #include "ui/base/class_property.h"
 #include "ui/views/widget/widget.h"
 
@@ -77,6 +78,17 @@
 void OnLacrosChromeAppWindowRemoved(const std::string& app_id,
                                     const std::string& window_id);
 
+// Invoked when a desk is being removed. `removing_desk_guid` is the GUID for
+// the removing desk.
+COMPONENT_EXPORT(APP_RESTORE)
+void SaveRemovingDeskGuid(const base::GUID& removing_desk_guid);
+
+// Invoked when desk removal is completed. The desk is either fully closed or
+// the removal has been undone. In either case, the full restore service should
+// no longer consider this desk to be in the process of removal.
+COMPONENT_EXPORT(APP_RESTORE)
+void ResetRemovingDeskGuid();
+
 }  // namespace full_restore
 
 #endif  // COMPONENTS_APP_RESTORE_FULL_RESTORE_UTILS_H_
diff --git a/components/app_restore/restore_data.cc b/components/app_restore/restore_data.cc
index b50e79e..9cb6d6a 100644
--- a/components/app_restore/restore_data.cc
+++ b/components/app_restore/restore_data.cc
@@ -25,6 +25,10 @@
 // a valid RWID should not use.
 int32_t g_desk_template_window_restore_id = -1;
 
+// Key used to record `removing_desk_guid_` when `RestoreData` is converted to
+// JSON.
+constexpr char kRemovingDeskGuidKey[] = "removing_desk_guid";
+
 }  // namespace
 
 RestoreData::RestoreData() = default;
@@ -37,9 +41,23 @@
     return;
   }
 
+  if (auto* removing_desk_guid_string =
+          dict->FindString(kRemovingDeskGuidKey)) {
+    removing_desk_guid_ =
+        base::GUID::ParseLowercase(*removing_desk_guid_string);
+  }
+
   for (auto iter : *dict) {
-    const std::string& app_id = iter.first;
-    base::Value::Dict* value = dict->FindDict(app_id);
+    // `key` can be an app ID or `kRemovingDeskGuidKey`.
+    const std::string& key = iter.first;
+    base::Value::Dict* value = iter.second.GetIfDict();
+
+    // Skip the removing desk GUID because we already covered this before the
+    // loop.
+    if (key == kRemovingDeskGuidKey) {
+      continue;
+    }
+
     if (!value) {
       DVLOG(0) << "Fail to parse full restore data. "
                << "Cannot find the app restore data dict.";
@@ -47,20 +65,32 @@
     }
 
     for (auto data_iter : *value) {
+      const std::string& window_id_string = data_iter.first;
+
       int window_id = 0;
-      if (!base::StringToInt(data_iter.first, &window_id)) {
+      if (!base::StringToInt(window_id_string, &window_id)) {
         DVLOG(0) << "Fail to parse full restore data. "
                  << "Cannot find the valid id.";
         continue;
       }
-      base::Value::Dict* app_restore_data = value->FindDict(data_iter.first);
-      if (!app_restore_data) {
+
+      base::Value::Dict* app_restore_data_dict = data_iter.second.GetIfDict();
+      if (!app_restore_data_dict) {
         DVLOG(0) << "Fail to parse app restore data. "
                  << "Cannot find the app restore data dict.";
         continue;
       }
-      app_id_to_launch_list_[app_id][window_id] =
-          std::make_unique<AppRestoreData>(std::move(*app_restore_data));
+
+      // If the data is for an app that was on a removing desk, then we can skip
+      // adding the data.
+      auto app_restore_data =
+          std::make_unique<AppRestoreData>(std::move(*app_restore_data_dict));
+      if (removing_desk_guid_.is_valid() &&
+          app_restore_data->desk_guid == removing_desk_guid_) {
+        continue;
+      }
+
+      app_id_to_launch_list_[key][window_id] = std::move(app_restore_data);
     }
   }
 }
@@ -75,6 +105,9 @@
           data_it.second->Clone();
     }
   }
+  if (removing_desk_guid_.is_valid()) {
+    restore_data->removing_desk_guid_ = removing_desk_guid_;
+  }
   return restore_data;
 }
 
@@ -92,6 +125,12 @@
 
     restore_data_dict.SetKey(it.first, std::move(info_dict));
   }
+
+  if (removing_desk_guid_.is_valid()) {
+    restore_data_dict.GetDict().Set(kRemovingDeskGuidKey,
+                                    removing_desk_guid_.AsLowercaseString());
+  }
+
   return restore_data_dict;
 }
 
@@ -297,8 +336,9 @@
 }
 
 std::string RestoreData::ToString() const {
-  if (app_id_to_launch_list_.empty())
+  if (app_id_to_launch_list_.empty() && !removing_desk_guid_.is_valid()) {
     return "empty";
+  }
 
   std::string result = "( ";
   for (const auto& entry : app_id_to_launch_list_) {
@@ -313,7 +353,16 @@
           windows.second->GetWindowInfo()->ToString();
     }
   }
-  return result + " )";
+
+  result += " )";
+
+  if (removing_desk_guid_.is_valid()) {
+    result +=
+        base::StringPrintf(" (Removing Desk GUID: %s)",
+                           removing_desk_guid_.AsLowercaseString().c_str());
+  }
+
+  return result;
 }
 
 AppRestoreData* RestoreData::GetAppRestoreDataMutable(const std::string& app_id,
diff --git a/components/app_restore/restore_data.h b/components/app_restore/restore_data.h
index 019d001..53b226c7 100644
--- a/components/app_restore/restore_data.h
+++ b/components/app_restore/restore_data.h
@@ -9,6 +9,7 @@
 #include <memory>
 
 #include "base/component_export.h"
+#include "base/guid.h"
 #include "base/values.h"
 #include "components/app_restore/app_restore_data.h"
 
@@ -40,7 +41,8 @@
 
   std::unique_ptr<RestoreData> Clone() const;
 
-  // Converts |app_id_to_launch_list_| to base::Value, e.g.:
+  // Converts `app_id_to_launch_list_` and `removing_desk_guid_` to base::Value,
+  // e.g.:
   // {
   //   "odknhmnlageboeamepcngndbggdpaobj":    // app_id
   //     {
@@ -51,6 +53,7 @@
   //           "display_id": "22000000",
   //           "index": 3,
   //           "desk_id": 1,
+  //           "desk_guid": "d782accb-232f-4f47-ad24-a7100f9c0ec0",
   //           "restored_bounds": { 0, 100, 200, 300 },
   //           "current_bounds": { 100, 200, 200, 300 },
   //           "window_state_type": 256,
@@ -70,6 +73,9 @@
   //           ...
   //         },
   //     },
+  //   ...
+  //   "removing_desk_guid":
+  //     "d782accb-232f-4f47-ad24-a7100f9c0ec0"  // removing_desk_guid
   // }
   base::Value ConvertToValue() const;
 
@@ -187,6 +193,10 @@
     return app_id_to_launch_list_;
   }
 
+  void set_removing_desk_guid(const base::GUID& removing_desk_guid) {
+    removing_desk_guid_ = removing_desk_guid;
+  }
+
  private:
   // Returns the pointer to AppRestoreData for the given |app_id| and
   // |window_id|. Returns null if there is no AppRestoreData.
@@ -197,6 +207,10 @@
 
   // Saves the next restore window_id to be handled for each chrome app.
   std::map<std::string, int> chrome_app_id_to_current_window_id_;
+
+  // The GUID of a desk that is being removed. This will only be valid if a desk
+  // is in the process of being removed.
+  base::GUID removing_desk_guid_;
 };
 
 }  // namespace app_restore
diff --git a/components/app_restore/restore_data_unittest.cc b/components/app_restore/restore_data_unittest.cc
index 5afe53c..a0bdf05 100644
--- a/components/app_restore/restore_data_unittest.cc
+++ b/components/app_restore/restore_data_unittest.cc
@@ -66,6 +66,10 @@
 constexpr int32_t kDeskId3 =
     aura::client::kWindowWorkspaceVisibleOnAllWorkspaces;
 
+const base::GUID kDeskGuid1 = base::GUID::GenerateRandomV4();
+const base::GUID kDeskGuid2 = base::GUID::GenerateRandomV4();
+const base::GUID kDeskGuid3 = base::GUID();
+
 constexpr gfx::Rect kCurrentBounds1(11, 21, 111, 121);
 constexpr gfx::Rect kCurrentBounds2(31, 41, 131, 141);
 constexpr gfx::Rect kCurrentBounds3(51, 61, 151, 161);
@@ -184,6 +188,7 @@
     WindowInfo window_info1;
     window_info1.activation_index = kActivationIndex1;
     window_info1.desk_id = kDeskId1;
+    window_info1.desk_guid = kDeskGuid1;
     window_info1.current_bounds = kCurrentBounds1;
     window_info1.window_state_type = kWindowStateType1;
     window_info1.display_id = kDisplayId2;
@@ -196,6 +201,7 @@
     WindowInfo window_info2;
     window_info2.activation_index = kActivationIndex2;
     window_info2.desk_id = kDeskId2;
+    window_info2.desk_guid = kDeskGuid2;
     window_info2.current_bounds = kCurrentBounds2;
     window_info2.window_state_type = kWindowStateType2;
     window_info2.pre_minimized_show_state_type = kPreMinimizedWindowStateType2;
@@ -208,6 +214,7 @@
     WindowInfo window_info3;
     window_info3.activation_index = kActivationIndex3;
     window_info3.desk_id = kDeskId3;
+    window_info3.desk_guid = kDeskGuid3;
     window_info3.current_bounds = kCurrentBounds3;
     window_info3.window_state_type = kWindowStateType3;
     window_info3.snap_percentage = kSnapPercentage;
@@ -236,6 +243,7 @@
       int32_t activation_index,
       int32_t first_non_pinned_tab_index,
       int32_t desk_id,
+      const base::GUID& desk_guid,
       const gfx::Rect& current_bounds,
       chromeos::WindowStateType window_state_type,
       ui::WindowShowState pre_minimized_show_state_type,
@@ -283,6 +291,8 @@
     EXPECT_TRUE(data->desk_id.has_value());
     EXPECT_EQ(desk_id, data->desk_id.value());
 
+    EXPECT_EQ(desk_guid, data->desk_guid);
+
     EXPECT_TRUE(data->current_bounds.has_value());
     EXPECT_EQ(current_bounds, data->current_bounds.value());
 
@@ -387,8 +397,8 @@
         std::vector<base::FilePath>{base::FilePath(kFilePath1),
                                     base::FilePath(kFilePath2)},
         MakeIntent(kIntentActionSend, kMimeType, kShareText1), kAppTypeBrower1,
-        kActivationIndex1, kFirstNonPinnedTabIndex, kDeskId1, kCurrentBounds1,
-        kWindowStateType1, kPreMinimizedWindowStateType1,
+        kActivationIndex1, kFirstNonPinnedTabIndex, kDeskId1, kDeskGuid1,
+        kCurrentBounds1, kWindowStateType1, kPreMinimizedWindowStateType1,
         /*snap_percentage=*/0, kMaxSize1, kMinSize1, std::u16string(kTitle1),
         kBoundsInRoot1, kPrimaryColor1, kStatusBarColor1,
         /*tab_group_infos=*/{});
@@ -403,8 +413,8 @@
         WindowOpenDisposition::NEW_FOREGROUND_TAB, kDisplayId1,
         std::vector<base::FilePath>{base::FilePath(kFilePath2)},
         MakeIntent(kIntentActionView, kMimeType, kShareText2), kAppTypeBrower2,
-        kActivationIndex2, kFirstNonPinnedTabIndex, kDeskId2, kCurrentBounds2,
-        kWindowStateType2, kPreMinimizedWindowStateType2,
+        kActivationIndex2, kFirstNonPinnedTabIndex, kDeskId2, kDeskGuid2,
+        kCurrentBounds2, kWindowStateType2, kPreMinimizedWindowStateType2,
         /*snap_percentage=*/0, absl::nullopt, kMinSize2,
         std::u16string(kTitle2), kBoundsInRoot2, kPrimaryColor2,
         kStatusBarColor2, std::move(expected_tab_group_infos),
@@ -423,9 +433,10 @@
         WindowOpenDisposition::NEW_POPUP, kDisplayId1,
         std::vector<base::FilePath>{base::FilePath(kFilePath1)},
         MakeIntent(kIntentActionView, kMimeType, kShareText1), kAppTypeBrower3,
-        kActivationIndex3, kFirstNonPinnedTabIndex, kDeskId3, kCurrentBounds3,
-        kWindowStateType3, kPreMinimizedWindowStateType3, kSnapPercentage,
-        absl::nullopt, absl::nullopt, absl::nullopt, absl::nullopt, 0, 0,
+        kActivationIndex3, kFirstNonPinnedTabIndex, kDeskId3, kDeskGuid3,
+        kCurrentBounds3, kWindowStateType3, kPreMinimizedWindowStateType3,
+        kSnapPercentage, absl::nullopt, absl::nullopt, absl::nullopt,
+        absl::nullopt, 0, 0,
         /*expected_tab_group_infos=*/{});
   }
 
@@ -486,10 +497,10 @@
       WindowOpenDisposition::NEW_FOREGROUND_TAB, kDisplayId1,
       std::vector<base::FilePath>{base::FilePath(kFilePath2)},
       MakeIntent(kIntentActionView, kMimeType, kShareText2), kAppTypeBrower2,
-      kActivationIndex2, kFirstNonPinnedTabIndex, kDeskId2, kCurrentBounds2,
-      kWindowStateType2, kPreMinimizedWindowStateType2, /*snap_percentage=*/0,
-      absl::nullopt, kMinSize2, std::u16string(kTitle2), kBoundsInRoot2,
-      kPrimaryColor2, kStatusBarColor2, /*tab_group_infos=*/{});
+      kActivationIndex2, kFirstNonPinnedTabIndex, kDeskId2, kDeskGuid2,
+      kCurrentBounds2, kWindowStateType2, kPreMinimizedWindowStateType2,
+      /*snap_percentage=*/0, absl::nullopt, kMinSize2, std::u16string(kTitle2),
+      kBoundsInRoot2, kPrimaryColor2, kStatusBarColor2, /*tab_group_infos=*/{});
 
   // Verify the restore data for |kAppId2| still exists.
   const auto launch_list_it2 =
@@ -570,6 +581,7 @@
   EXPECT_TRUE(window_info->activation_index.has_value());
   EXPECT_EQ(INT32_MAX, window_info->activation_index.value());
   EXPECT_TRUE(window_info->desk_id.has_value());
+  EXPECT_TRUE(window_info->desk_guid.is_valid());
   EXPECT_TRUE(window_info->current_bounds.has_value());
   EXPECT_TRUE(window_info->window_state_type.has_value());
   EXPECT_TRUE(window_info->arc_extra_info.has_value());
@@ -675,6 +687,7 @@
   EXPECT_TRUE(window_info);
   EXPECT_FALSE(window_info->activation_index.has_value());
   EXPECT_FALSE(window_info->desk_id.has_value());
+  EXPECT_FALSE(window_info->desk_guid.is_valid());
   EXPECT_FALSE(window_info->current_bounds.has_value());
   EXPECT_FALSE(window_info->window_state_type.has_value());
 
@@ -689,6 +702,9 @@
   EXPECT_TRUE(window_info->desk_id.has_value());
   EXPECT_EQ(kDeskId1, window_info->desk_id.value());
 
+  EXPECT_TRUE(window_info->desk_guid.is_valid());
+  EXPECT_EQ(kDeskGuid1, window_info->desk_guid);
+
   EXPECT_TRUE(window_info->current_bounds.has_value());
   EXPECT_EQ(kCurrentBounds1, window_info->current_bounds.value());
 
diff --git a/components/app_restore/window_info.cc b/components/app_restore/window_info.cc
index c061a30d..cd791e59 100644
--- a/components/app_restore/window_info.cc
+++ b/components/app_restore/window_info.cc
@@ -40,6 +40,11 @@
   return prefix + ": " + base::UTF16ToASCII(val.value_or(u""));
 }
 
+std::string ToPrefixedString(base::GUID val, const std::string& prefix) {
+  return prefix + ": " +
+         (val.is_valid() ? val : base::GUID()).AsLowercaseString();
+}
+
 }  // namespace
 
 WindowInfo::ArcExtraInfo::ArcExtraInfo() = default;
@@ -58,6 +63,7 @@
   new_window_info->window = window;
   new_window_info->activation_index = activation_index;
   new_window_info->desk_id = desk_id;
+  new_window_info->desk_guid = desk_guid;
   new_window_info->current_bounds = current_bounds;
   new_window_info->window_state_type = window_state_type;
   new_window_info->pre_minimized_show_state_type =
@@ -72,6 +78,7 @@
 std::string WindowInfo::ToString() const {
   return ToPrefixedString(activation_index, "Activation index") +
          ToPrefixedString(desk_id, "Desk") +
+         ToPrefixedString(desk_guid, "Desk guid") +
          ToPrefixedString(current_bounds, "Current bounds") +
          ToPrefixedString(window_state_type, "Window state") +
          ToPrefixedString(pre_minimized_show_state_type,
diff --git a/components/app_restore/window_info.h b/components/app_restore/window_info.h
index 83e57732..57bf6c8 100644
--- a/components/app_restore/window_info.h
+++ b/components/app_restore/window_info.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_APP_RESTORE_WINDOW_INFO_H_
 #define COMPONENTS_APP_RESTORE_WINDOW_INFO_H_
 
+#include "base/guid.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/aura/window.h"
@@ -45,6 +46,9 @@
   // Virtual desk id.
   absl::optional<int32_t> desk_id;
 
+  // Virtual desk guid.
+  base::GUID desk_guid;
+
   // Current bounds in screen in coordinates. If the window has restore bounds,
   // then this contains the restore bounds.
   absl::optional<gfx::Rect> current_bounds;
diff --git a/components/autofill/android/java/res/values/dimens.xml b/components/autofill/android/java/res/values/dimens.xml
index 46634d4..97cfc4c 100644
--- a/components/autofill/android/java/res/values/dimens.xml
+++ b/components/autofill/android/java/res/values/dimens.xml
@@ -52,4 +52,10 @@
     <dimen name="settings_page_card_icon_height_new">24dp</dimen>
     <dimen name="settings_page_card_icon_end_margin">20dp</dimen>
     <dimen name="settings_page_margin_between_card_name_and_last_four_digits">4dp</dimen>
+
+    <!-- Card Unmask dialog -->
+    <dimen name="card_unmask_dialog_credit_card_icon_width">32dp</dimen>
+    <dimen name="card_unmask_dialog_credit_card_icon_height">20dp</dimen>
+    <dimen name="card_unmask_dialog_credit_card_icon_width_new">40dp</dimen>
+    <dimen name="card_unmask_dialog_credit_card_icon_height_new">24dp</dimen>
 </resources>
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index 8034d15..b4e9b57 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -47,6 +47,14 @@
              "AutofillEnableCardProductName",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// When enabled, if the user encounters the yellow path (challenge path) in the
+// VCN retrieval flow and the server denotes that the card is eligible for email
+// OTP authentication, email OTP authentication will be offered as one of the
+// challenge options.
+BASE_FEATURE(kAutofillEnableEmailOtpForVcnYellowPath,
+             "AutofillEnableEmailOtpForVcnYellowPath",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // When enabled, user's will see network card art images and network icons which
 // are larger, having a white border, and don't have the standard grey overlay
 // applied to them.
diff --git a/components/autofill/core/common/autofill_payments_features.h b/components/autofill/core/common/autofill_payments_features.h
index fcd0597..247fab5 100644
--- a/components/autofill/core/common/autofill_payments_features.h
+++ b/components/autofill/core/common/autofill_payments_features.h
@@ -17,6 +17,7 @@
 BASE_DECLARE_FEATURE(kAutofillAutoTriggerManualFallbackForCards);
 BASE_DECLARE_FEATURE(kAutofillEnableCardArtImage);
 BASE_DECLARE_FEATURE(kAutofillEnableCardProductName);
+BASE_DECLARE_FEATURE(kAutofillEnableEmailOtpForVcnYellowPath);
 BASE_DECLARE_FEATURE(kAutofillEnableFIDOProgressDialog);
 BASE_DECLARE_FEATURE(kAutofillEnableIbanClientSideUrlFiltering);
 BASE_DECLARE_FEATURE(kAutofillEnableManualFallbackForVirtualCards);
diff --git a/components/browser_ui/widget/android/BUILD.gn b/components/browser_ui/widget/android/BUILD.gn
index a7eba723..519b460 100644
--- a/components/browser_ui/widget/android/BUILD.gn
+++ b/components/browser_ui/widget/android/BUILD.gn
@@ -98,6 +98,7 @@
     "java/src/org/chromium/components/browser_ui/widget/scrim/ScrimProperties.java",
     "java/src/org/chromium/components/browser_ui/widget/scrim/ScrimView.java",
     "java/src/org/chromium/components/browser_ui/widget/scrim/ScrimViewBinder.java",
+    "java/src/org/chromium/components/browser_ui/widget/selectable_list/CheckableSelectableItemView.java",
     "java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemView.java",
     "java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBase.java",
     "java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewHolder.java",
@@ -239,7 +240,6 @@
     "java/res/layout/indeterminate_progress_view.xml",
     "java/res/layout/list_menu_item.xml",
     "java/res/layout/modern_list_item_view.xml",
-    "java/res/layout/modern_list_item_view_v2.xml",
     "java/res/layout/more_progress_button.xml",
     "java/res/layout/number_roll_view.xml",
     "java/res/layout/promo_card_view_compact.xml",
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/CheckableSelectableItemView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/CheckableSelectableItemView.java
new file mode 100644
index 0000000..3740b47
--- /dev/null
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/CheckableSelectableItemView.java
@@ -0,0 +1,119 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.browser_ui.widget.selectable_list;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LevelListDrawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.widget.ImageViewCompat;
+import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
+
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
+import org.chromium.components.browser_ui.widget.R;
+
+/**
+ * Implementation of SelectableItemViewBase which requires the caller to inflate their own view.
+ * Useful for when you need to diverge from the built-in layout of SelectableItemView, but want
+ * the checkmark functionality. Backgrounds used for the #iconView should be instances of
+ * LevelListDrawable to properly interact with the checkmark behavior, see list_item_icon_modern_bg
+ * for an example of how to setup a new drawable.
+ *
+ * @param <E> The type of the item associated with this SelectableItemViewBase.
+ */
+public abstract class CheckableSelectableItemView<E> extends SelectableItemViewBase<E> {
+    private final AnimatedVectorDrawableCompat mCheckDrawable;
+
+    /** The color state list for the start icon view when the item is selected. */
+    private ColorStateList mIconSelectedColorList;
+
+    /** Drawable for the start icon */
+    private @Nullable Drawable mIconDrawable;
+
+    /** Constructor for inflating from XML. */
+    public CheckableSelectableItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mIconSelectedColorList =
+                ColorStateList.valueOf(SemanticColorUtils.getDefaultIconColorInverse(context));
+        mCheckDrawable = AnimatedVectorDrawableCompat.create(
+                getContext(), R.drawable.ic_check_googblue_24dp_animated);
+    }
+
+    // Abstract methods.
+
+    /** Returns the icon view used to show the checkmark when selected. */
+    protected abstract ImageView getIconView();
+
+    /**
+     * Returns the default tint for the icon view, can be null. Will be swapped with a different
+     * tint when the checkmark is shown.
+     */
+    protected abstract @Nullable ColorStateList getDefaultIconTint();
+
+    /**
+     * Returns the selected level for the icon view background. This level is used when the
+     * drawable set by #setIconDrawable is displayed. The background for the ImageView returned
+     * by #getIconView should be a LevelListDrawable that supports both the default and selected
+     * state.
+     */
+    protected abstract int getDefaultLevel();
+
+    /**
+     * Returns the selected level for the icon view background. This level is used when the
+     * checkmark is displayed. The background for the ImageView returned by #getIconView should
+     * be a LevelListDrawable that supports both the default and selected state.
+     */
+    protected abstract int getSelectedLevel();
+
+    /**
+     * Set drawable for the start icon view. Note that you may need to use this method instead of
+     * mIconView#setImageDrawable to ensure icon view is correctly set in selection mode.
+     */
+    public void setIconDrawable(Drawable iconDrawable) {
+        mIconDrawable = iconDrawable;
+        updateView(false);
+    }
+
+    /** Returns the drawable set for the start icon view, if any. */
+    public Drawable getIconDrawable() {
+        return mIconDrawable;
+    }
+
+    @Override
+    protected void updateView(boolean animate) {
+        ImageView iconView = getIconView();
+        if (iconView == null) return;
+        Drawable iconViewBackground = iconView.getBackground();
+        assert iconViewBackground instanceof LevelListDrawable;
+
+        boolean levelMatches;
+        boolean levelChangeSuccess;
+        if (isChecked()) {
+            int level = getSelectedLevel();
+            levelMatches = level == iconViewBackground.getLevel();
+            levelChangeSuccess = iconViewBackground.setLevel(level);
+            iconView.setImageDrawable(mCheckDrawable);
+            ImageViewCompat.setImageTintList(iconView, mIconSelectedColorList);
+            if (animate) mCheckDrawable.start();
+        } else {
+            int level = getDefaultLevel();
+            levelMatches = level == iconViewBackground.getLevel();
+            levelChangeSuccess = iconViewBackground.setLevel(level);
+            iconView.setImageDrawable(mIconDrawable);
+            ImageViewCompat.setImageTintList(iconView, getDefaultIconTint());
+        }
+        assert levelMatches || levelChangeSuccess;
+    }
+
+    @VisibleForTesting
+    public void endAnimationsForTests() {
+        mCheckDrawable.stop();
+    }
+}
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemView.java
index 20c3c87..5db0026 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemView.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemView.java
@@ -9,22 +9,15 @@
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.widget.AppCompatImageButton;
 import androidx.core.widget.ImageViewCompat;
-import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
 
-import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.R;
-import org.chromium.components.browser_ui.widget.TintedDrawable;
-import org.chromium.ui.base.ViewUtils;
 
 /**
  * Default implementation of SelectableItemViewBase. Contains a start icon, title, description, and
@@ -32,111 +25,70 @@
  *
  * @param <E> The type of the item associated with this SelectableItemViewBase.
  */
-public abstract class SelectableItemView<E> extends SelectableItemViewBase<E> {
-    protected final int mDefaultLevel;
-    protected final int mSelectedLevel;
-    protected final AnimatedVectorDrawableCompat mCheckDrawable;
-
-    protected int mStartIconViewSize;
-
-    /**
-     * The LinearLayout containing the rest of the views for the selectable item.
-     */
+public abstract class SelectableItemView<E> extends CheckableSelectableItemView<E> {
+    /** The LinearLayout containing the rest of the views for the selectable item. */
     protected LinearLayout mContentView;
 
-    /**
-     * An icon displayed at the start of the item row.
-     */
+    /** An icon displayed at the start of the item row. */
     protected ImageView mStartIconView;
 
-    /**
-     * An optional button displayed at the before the end button, GONE by default.
-     */
-    protected AppCompatImageButton mEndStartButtonView;
-
-    /**
-     * An optional button displayed at the end of the item row, GONE by default.
-     */
+    /** An optional button displayed at the end of the item row, GONE by default. */
     protected AppCompatImageButton mEndButtonView;
 
-    /**
-     * A title line displayed between the start and (optional) end icon.
-     */
+    /** A title line displayed between the start and (optional) end icon. */
     protected TextView mTitleView;
 
-    /**
-     * A description line displayed below the title line.
-     */
+    /** A description line displayed below the title line. */
     protected TextView mDescriptionView;
 
-    /**
-     * The color state list for the start icon view when the item is selected.
-     */
-    protected ColorStateList mStartIconSelectedColorList;
-
-    private Drawable mStartIconDrawable;
-
-    /**
-     * Layout res to be used when inflating the view, used to swap in the visual refresh.
-     */
+    /** Layout res to be used when inflating the view, used to swap in the visual refresh. */
     private int mLayoutRes;
-
-    /**
-     * The resource for the start icon background.
-     */
+    /** Levels for the background. */
+    private final int mDefaultLevel;
+    private final int mSelectedLevel;
+    /** The resource for the start icon background. */
     private int mStartIconBackgroundRes;
 
     /**
-     * Tracks if inflation is finished.
-     */
-    private boolean mInflationFinished;
-
-    /**
-     * Tracks if the visual refresh is enabled.
-     */
-    private boolean mVisualRefreshEnabled;
-
-    /**
-     * Container for custom content to be set on the view.
-     */
-    private ViewGroup mCustomContentContainer;
-
-    /**
      * Constructor for inflating from XML.
      */
     public SelectableItemView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mStartIconSelectedColorList =
-                ColorStateList.valueOf(SemanticColorUtils.getDefaultIconColorInverse(context));
+
+        mLayoutRes = R.layout.modern_list_item_view;
+        mStartIconBackgroundRes = R.drawable.list_item_icon_modern_bg;
         mDefaultLevel = getResources().getInteger(R.integer.list_item_level_default);
         mSelectedLevel = getResources().getInteger(R.integer.list_item_level_selected);
-        mCheckDrawable = AnimatedVectorDrawableCompat.create(
-                getContext(), R.drawable.ic_check_googblue_24dp_animated);
-        mStartIconBackgroundRes = R.drawable.list_item_icon_modern_bg;
-        mLayoutRes = R.layout.modern_list_item_view;
     }
 
-    protected boolean isVisualRefreshEnabled() {
-        return mVisualRefreshEnabled;
+    // CheckableSelectableItemView implementation.
+
+    @Override
+    protected ImageView getIconView() {
+        return mStartIconView;
     }
 
-    protected void enableVisualRefresh() {
-        mVisualRefreshEnabled = true;
+    @Override
+    protected @Nullable ColorStateList getDefaultIconTint() {
+        return null;
+    }
 
-        mStartIconBackgroundRes = R.drawable.list_item_icon_modern_bg_rect;
-        mLayoutRes = R.layout.modern_list_item_view_v2;
-        if (mInflationFinished) {
-            removeAllViews();
-            inflateAndPopulateViewVariables();
-        }
+    @Override
+    protected int getSelectedLevel() {
+        return mSelectedLevel;
+    }
+
+    @Override
+    protected int getDefaultLevel() {
+        return mDefaultLevel;
     }
 
     // FrameLayout implementations.
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         inflateAndPopulateViewVariables();
-        mInflationFinished = true;
     }
 
     private void inflateAndPopulateViewVariables() {
@@ -150,14 +102,7 @@
 
         if (mStartIconView != null) {
             mStartIconView.setBackgroundResource(mStartIconBackgroundRes);
-            ImageViewCompat.setImageTintList(mStartIconView, getDefaultStartIconTint());
-        }
-
-        if (isVisualRefreshEnabled()) {
-            mEndStartButtonView = findViewById(R.id.optional_button);
-            mCustomContentContainer = findViewById(R.id.custom_content_container);
-            ViewUtils.requestLayout(
-                    mStartIconView, "SelectableItemView.inflateAndPopulateViewVariables");
+            ImageViewCompat.setImageTintList(mStartIconView, getDefaultIconTint());
         }
     }
 
@@ -166,87 +111,13 @@
      * mIconView#setImageDrawable to ensure icon view is correctly set in selection mode.
      */
     protected void setStartIconDrawable(Drawable iconDrawable) {
-        mStartIconDrawable = iconDrawable;
-        updateView(false);
+        setIconDrawable(iconDrawable);
     }
 
     /**
      * Returns the drawable set for the start icon view, if any.
      */
     protected Drawable getStartIconDrawable() {
-        return mStartIconDrawable;
-    }
-
-    /**
-     * Sets a custom content view.
-     * @param view The custom view or null to clear it.
-     */
-    protected void setCustomContent(@Nullable View view) {
-        assert isVisualRefreshEnabled()
-            : "Specifying custom content is only allowed when visual refresh is enabled";
-
-        // Custom content is allowed only with the visual refresh.
-        if (!isVisualRefreshEnabled()) return;
-
-        mCustomContentContainer.removeAllViews();
-        if (view == null) return;
-        mCustomContentContainer.addView(view);
-    }
-
-    /**
-     * Update start icon image and background based on whether this item is selected.
-     */
-    @Override
-    protected void updateView(boolean animate) {
-        // TODO(huayinz): Refactor this method so that mIconView is not exposed to subclass.
-        if (mStartIconView == null) return;
-
-        if (isChecked()) {
-            mStartIconView.getBackground().setLevel(mSelectedLevel);
-            mStartIconView.setImageDrawable(mCheckDrawable);
-            ImageViewCompat.setImageTintList(mStartIconView, mStartIconSelectedColorList);
-            if (animate) mCheckDrawable.start();
-        } else {
-            mStartIconView.getBackground().setLevel(mDefaultLevel);
-            mStartIconView.setImageDrawable(mStartIconDrawable);
-            ImageViewCompat.setImageTintList(mStartIconView, getDefaultStartIconTint());
-        }
-    }
-
-    /**
-     * @return The {@link ColorStateList} used to tint the start icon drawable set via
-     *         {@link #setStartIconDrawable(Drawable)} when the item is not selected.
-     */
-    protected @Nullable ColorStateList getDefaultStartIconTint() {
-        return null;
-    }
-
-    @VisibleForTesting
-    public void endAnimationsForTests() {
-        mCheckDrawable.stop();
-    }
-
-    /**
-     * Sets the icon for the image view: the default icon if unselected, the check mark if selected.
-     *
-     * @param imageView     The image view in which the icon will be presented.
-     * @param defaultIcon   The default icon that will be displayed if not selected.
-     * @param isSelected    Whether the item is selected or not.
-     */
-    public static void applyModernIconStyle(
-            ImageView imageView, Drawable defaultIcon, boolean isSelected) {
-        imageView.setBackgroundResource(R.drawable.list_item_icon_modern_bg);
-        Drawable drawable;
-        if (isSelected) {
-            drawable = TintedDrawable.constructTintedDrawable(
-                    imageView.getContext(), R.drawable.ic_check_googblue_24dp);
-            drawable.setTint(SemanticColorUtils.getDefaultIconColorInverse(imageView.getContext()));
-        } else {
-            drawable = defaultIcon;
-        }
-        imageView.setImageDrawable(drawable);
-        imageView.getBackground().setLevel(isSelected
-                        ? imageView.getResources().getInteger(R.integer.list_item_level_selected)
-                        : imageView.getResources().getInteger(R.integer.list_item_level_default));
+        return getIconDrawable();
     }
 }
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBase.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBase.java
index e5ed4840..1988e020 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBase.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBase.java
@@ -55,6 +55,23 @@
      */
     public SelectableItemViewBase(Context context, AttributeSet attrs) {
         super(context, attrs);
+
+        setOnTouchListener(this);
+        setOnClickListener(this);
+        setOnLongClickListener(this);
+        setAccessibilityDelegate(new AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+
+                // Announce checked state if selection mode is on. The actual read out from talkback
+                // is "checked/unchecked, {content description of this view.}"
+                boolean checkable = mSelectionDelegate != null
+                        && mSelectionDelegate.isSelectionEnabled() && mItem != null;
+                info.setCheckable(checkable);
+                info.setChecked(isChecked());
+            }
+        });
     }
 
     /**
@@ -103,29 +120,6 @@
         return mItem;
     }
 
-    // FrameLayout implementations.
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        setOnTouchListener(this);
-        setOnClickListener(this);
-        setOnLongClickListener(this);
-        setAccessibilityDelegate(new AccessibilityDelegate() {
-            @Override
-            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-                super.onInitializeAccessibilityNodeInfo(host, info);
-
-                // Announce checked state if selection mode is on. The actual read out from talkback
-                // is "checked/unchecked, {content description of this view.}"
-                boolean checkable = mSelectionDelegate != null
-                        && mSelectionDelegate.isSelectionEnabled() && mItem != null;
-                info.setCheckable(checkable);
-                info.setChecked(isChecked());
-            }
-        });
-    }
-
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -155,6 +149,7 @@
     }
 
     // OnClickListener implementation.
+
     @Override
     public void onClick(View view) {
         assert view == this;
diff --git a/components/browsing_topics/browsing_topics_service_impl.cc b/components/browsing_topics/browsing_topics_service_impl.cc
index 347646c..78251208 100644
--- a/components/browsing_topics/browsing_topics_service_impl.cc
+++ b/components/browsing_topics/browsing_topics_service_impl.cc
@@ -245,7 +245,14 @@
   // request.
   kObserveViaFetchLikeApi = 3,
 
-  kMaxValue = kObserveViaFetchLikeApi,
+  // Get topics via <iframe src=[url] browsingtopics>.
+  kGetViaIframeAttributeApi = 4,
+
+  // Observe topics via the "Sec-Browsing-Topics: ?1" response header for the
+  // <iframe src=[url] browsingtopics> request.
+  kObserveViaIframeAttributeApi = 5,
+
+  kMaxValue = kObserveViaIframeAttributeApi,
 };
 
 void RecordBrowsingTopicsApiActionTypeMetrics(ApiCallerSource caller_source,
@@ -271,6 +278,24 @@
     return;
   }
 
+  if (caller_source == ApiCallerSource::kIframeAttribute) {
+    if (get_topics) {
+      DCHECK(!observe);
+
+      base::UmaHistogramEnumeration(
+          kBrowsingTopicsApiActionTypeHistogramId,
+          BrowsingTopicsApiActionType::kGetViaIframeAttributeApi);
+      return;
+    }
+
+    DCHECK(observe);
+    base::UmaHistogramEnumeration(
+        kBrowsingTopicsApiActionTypeHistogramId,
+        BrowsingTopicsApiActionType::kObserveViaIframeAttributeApi);
+
+    return;
+  }
+
   DCHECK_EQ(caller_source, ApiCallerSource::kFetch);
 
   if (get_topics) {
diff --git a/components/browsing_topics/common/common_types.h b/components/browsing_topics/common/common_types.h
index 8af8511..c5cd65aa 100644
--- a/components/browsing_topics/common/common_types.h
+++ b/components/browsing_topics/common/common_types.h
@@ -26,6 +26,9 @@
   // fetch(<url>, {browsingTopics: true}), or XMLHttpRequest.send() with the
   // `deprecatedBrowsingTopics` property set to true.
   kFetch,
+
+  // The API usage is from <iframe src=[url] browsingtopics>.
+  kIframeAttribute,
 };
 
 // Represents the different reasons why the topics API access is denied. These
diff --git a/components/history_clusters/core/config.cc b/components/history_clusters/core/config.cc
index 56e59580..4401db2 100644
--- a/components/history_clusters/core/config.cc
+++ b/components/history_clusters/core/config.cc
@@ -89,7 +89,12 @@
   }
 
   // The `kJourneysImages` feature.
-  { images = base::FeatureList::IsEnabled(internal::kJourneysImages); }
+  {
+    images = base::FeatureList::IsEnabled(internal::kJourneysImages);
+
+    images_cover = GetFieldTrialParamByFeatureAsBool(
+        internal::kJourneysImages, "JourneysImagesCover", images_cover);
+  }
 
   // The `kPersistedClusters` feature and child params.
   {
diff --git a/components/history_clusters/core/config.h b/components/history_clusters/core/config.h
index 72a5730..f05060d 100644
--- a/components/history_clusters/core/config.h
+++ b/components/history_clusters/core/config.h
@@ -90,6 +90,9 @@
   // a proof of concept implementation for Entities only).
   bool images = false;
 
+  // Whether the image covers the whole icon container.
+  bool images_cover = true;
+
   // The `kPersistedClusters` feature and child params.
 
   // If enabled, updating clusters will persist the results to the history DB
diff --git a/components/history_clusters/core/features.cc b/components/history_clusters/core/features.cc
index 4089407..bcc9be32 100644
--- a/components/history_clusters/core/features.cc
+++ b/components/history_clusters/core/features.cc
@@ -37,6 +37,9 @@
              "JourneysImages",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+const base::FeatureParam<bool> kJourneysImagesCover{
+    &kJourneysImages, "JourneysImagesCover", true};
+
 BASE_FEATURE(kPersistedClusters,
              "HistoryClustersPersistedClusters",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/history_clusters/core/features.h b/components/history_clusters/core/features.h
index 5780e224..9e84061d 100644
--- a/components/history_clusters/core/features.h
+++ b/components/history_clusters/core/features.h
@@ -24,6 +24,9 @@
 // Enables images for Journeys in UI.
 BASE_DECLARE_FEATURE(kJourneysImages);
 
+// Enables images to cover the full container for Journeys in UI.
+extern const base::FeatureParam<bool> kJourneysImagesCover;
+
 // Enables persisting and using persisted clusters.
 BASE_DECLARE_FEATURE(kPersistedClusters);
 
diff --git a/components/memory_pressure/system_memory_pressure_evaluator_win.h b/components/memory_pressure/system_memory_pressure_evaluator_win.h
index d1419cac..3a0f8d5 100644
--- a/components/memory_pressure/system_memory_pressure_evaluator_win.h
+++ b/components/memory_pressure/system_memory_pressure_evaluator_win.h
@@ -5,7 +5,6 @@
 #ifndef COMPONENTS_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_WIN_H_
 #define COMPONENTS_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_WIN_H_
 
-#include "base/base_export.h"
 #include "base/functional/callback_forward.h"
 #include "base/memory/memory_pressure_listener.h"
 #include "base/memory/weak_ptr.h"
diff --git a/components/mirroring/service/media_remoter.cc b/components/mirroring/service/media_remoter.cc
index e372358..596c5116 100644
--- a/components/mirroring/service/media_remoter.cc
+++ b/components/mirroring/service/media_remoter.cc
@@ -147,7 +147,11 @@
   audio_config_ = absl::nullopt;
   video_config_ = absl::nullopt;
 
-  state_ = STOPPING_REMOTING;
+  // Don't change `state_` if remoting is disabled so that it won't attempt to
+  // start remoting again after mirroring resumed.
+  if (state_ != REMOTING_DISABLED) {
+    state_ = STOPPING_REMOTING;
+  }
   remoting_source_->OnStopped(reason);
   // Prevent the start of remoting until switching completes.
   remoting_source_->OnSinkGone();
diff --git a/components/mirroring/service/media_remoter_unittest.cc b/components/mirroring/service/media_remoter_unittest.cc
index 75361fda..e6f3e0b 100644
--- a/components/mirroring/service/media_remoter_unittest.cc
+++ b/components/mirroring/service/media_remoter_unittest.cc
@@ -233,8 +233,11 @@
   }
 
   // Signals that mirroring is resumed successfully.
-  void MirroringResumed() {
-    EXPECT_CALL(remoting_source_, OnSinkAvailable(_));
+  void MirroringResumed(bool is_remoting_disabled) {
+    // When MediaRemoter is in the REMOTING_DISABLED state, it should not notify
+    // its remoting_source_ about available sinks.
+    EXPECT_CALL(remoting_source_, OnSinkAvailable(_))
+        .Times(is_remoting_disabled ? 0 : 1);
     media_remoter_->OnMirroringResumed();
     task_environment_.RunUntilIdle();
     Mock::VerifyAndClear(&remoting_source_);
@@ -361,7 +364,7 @@
   StopRemoting();
 
   // Signals that successfully switch to mirroring.
-  MirroringResumed();
+  MirroringResumed(/* is_remoting_disabled */ false);
   // Now remoting can be started again.
   StartRemoting();
 }
@@ -370,6 +373,8 @@
   CreateRemoter();
   StartRemoting();
   RemotingStartFailed();
+  StopRemoting();
+  MirroringResumed(/* is_remoting_disabled */ true);
 }
 
 TEST_P(MediaRemoterTest, SwitchBetweenMultipleSessions) {
@@ -386,7 +391,7 @@
 
   // Stop the remoting session and switch to mirroring.
   StopRemoting();
-  MirroringResumed();
+  MirroringResumed(/* is_remoting_disabled */ false);
 
   // Switch to remoting again.
   StartRemoting();
@@ -396,7 +401,7 @@
 
   // Switch to mirroring again.
   StopRemoting();
-  MirroringResumed();
+  MirroringResumed(/* is_remoting_disabled */ false);
 }
 
 INSTANTIATE_TEST_SUITE_P(MediaRemoter,
diff --git a/components/ntp_snippets/user_classifier.cc b/components/ntp_snippets/user_classifier.cc
index 848b6a7..eb51742 100644
--- a/components/ntp_snippets/user_classifier.cc
+++ b/components/ntp_snippets/user_classifier.cc
@@ -11,7 +11,6 @@
 #include "base/cxx17_backports.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/clock.h"
 #include "components/ntp_snippets/features.h"
@@ -54,14 +53,6 @@
 const char kRareUserOpensNTPAtMostOncePerHoursParam[] =
     "user_classifier_rare_user_opens_ntp_at_most_once_per_hours";
 
-// Histograms for logging the estimated average hours to next event.
-const char kHistogramAverageHoursToOpenNTP[] =
-    "NewTabPage.UserClassifier.AverageHoursToOpenNTP";
-const char kHistogramAverageHoursToShowSuggestions[] =
-    "NewTabPage.UserClassifier.AverageHoursToShowSuggestions";
-const char kHistogramAverageHoursToUseSuggestions[] =
-    "NewTabPage.UserClassifier.AverageHoursToUseSuggestions";
-
 // The enum used for iteration.
 const UserClassifier::Metric kMetrics[] = {
     UserClassifier::Metric::NTP_OPENED,
@@ -238,29 +229,7 @@
 
 void UserClassifier::OnEvent(Metric metric) {
   DCHECK_NE(metric, Metric::COUNT);
-  double metric_value = UpdateMetricOnEvent(metric);
-
-  double avg = GetEstimateHoursBetweenEvents(
-      metric_value, discount_rate_per_hour_, min_hours_, max_hours_);
-  // We use kMaxHours as the max value below as the maximum value for the
-  // histograms must be constant.
-  switch (metric) {
-    case Metric::NTP_OPENED:
-      UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToOpenNTP, avg, 1,
-                                  kMaxHours, 50);
-      break;
-    case Metric::SUGGESTIONS_SHOWN:
-      UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToShowSuggestions, avg,
-                                  1, kMaxHours, 50);
-      break;
-    case Metric::SUGGESTIONS_USED:
-      UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToUseSuggestions, avg,
-                                  1, kMaxHours, 50);
-      break;
-    case Metric::COUNT:
-      NOTREACHED();
-      break;
-  }
+  UpdateMetricOnEvent(metric);
 }
 
 double UserClassifier::GetEstimatedAvgTime(Metric metric) const {
@@ -314,17 +283,17 @@
   }
 }
 
-double UserClassifier::UpdateMetricOnEvent(Metric metric) {
+void UserClassifier::UpdateMetricOnEvent(Metric metric) {
   // The pref_service_ can be null in tests.
   if (!pref_service_) {
-    return 0;
+    return;
   }
 
   double hours_since_last_time =
       std::min(max_hours_, GetHoursSinceLastTime(metric));
   // Ignore events within the same "browsing session".
   if (hours_since_last_time < min_hours_) {
-    return GetUpToDateMetricValue(metric);
+    return;
   }
 
   SetLastTimeToNow(metric);
@@ -335,7 +304,6 @@
       1 + DiscountMetric(metric_value, hours_since_last_time,
                          discount_rate_per_hour_);
   SetMetricValue(metric, new_metric_value);
-  return new_metric_value;
 }
 
 double UserClassifier::GetUpToDateMetricValue(Metric metric) const {
diff --git a/components/ntp_snippets/user_classifier.h b/components/ntp_snippets/user_classifier.h
index 4cbba3d..e1c3e18 100644
--- a/components/ntp_snippets/user_classifier.h
+++ b/components/ntp_snippets/user_classifier.h
@@ -20,8 +20,8 @@
 namespace ntp_snippets {
 
 // Collects data about user usage patterns of content suggestions, computes
-// long-term user metrics locally using pref, and reports the metrics to UMA.
-// Based on these long-term user metrics, it classifies the user in a UserClass.
+// long-term user metrics locally using pref. Based on these long-term user
+// metrics, it classifies the user in a UserClass.
 class UserClassifier {
  public:
   // Enumeration listing user classes
@@ -40,8 +40,7 @@
   // event in this time interval.
   // See https://en.wikipedia.org/wiki/Exponential_discounting for more details.
   // We keep track of the following events.
-  // NOTE: if you add any element, add it also in the static arrays in .cc and
-  // create another histogram.
+  // NOTE: if you add any element, add it also in the static arrays in .cc.
   enum class Metric {
     NTP_OPENED,  // When the user opens a new NTP - this indicates potential
                  // use of content suggestions.
@@ -82,7 +81,7 @@
  private:
   // The event has happened, recompute the metric accordingly. Then store and
   // return the new value.
-  double UpdateMetricOnEvent(Metric metric);
+  void UpdateMetricOnEvent(Metric metric);
   // No event has happened but we need to get up-to-date metric, recompute and
   // return the new value. This function does not store the recomputed metric.
   double GetUpToDateMetricValue(Metric metric) const;
diff --git a/components/ntp_snippets/user_classifier_unittest.cc b/components/ntp_snippets/user_classifier_unittest.cc
index 694c8d7..1005a7b 100644
--- a/components/ntp_snippets/user_classifier_unittest.cc
+++ b/components/ntp_snippets/user_classifier_unittest.cc
@@ -8,7 +8,6 @@
 #include <string>
 #include <utility>
 
-#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
@@ -140,8 +139,7 @@
 
 class UserClassifierMetricTest
     : public UserClassifierTest,
-      public ::testing::WithParamInterface<
-          std::pair<UserClassifier::Metric, std::string>> {
+      public ::testing::WithParamInterface<UserClassifier::Metric> {
  public:
   UserClassifierMetricTest() = default;
   UserClassifierMetricTest(const UserClassifierMetricTest&) = delete;
@@ -149,7 +147,7 @@
 };
 
 TEST_P(UserClassifierMetricTest, ShouldDecreaseEstimateAfterEvent) {
-  UserClassifier::Metric metric = GetParam().first;
+  UserClassifier::Metric metric = GetParam();
   UserClassifier* user_classifier = CreateUserClassifier();
 
   // The initial event does not decrease the estimate.
@@ -163,18 +161,8 @@
   }
 }
 
-TEST_P(UserClassifierMetricTest, ShouldReportToUmaOnEvent) {
-  UserClassifier::Metric metric = GetParam().first;
-  const std::string& histogram_name = GetParam().second;
-  base::HistogramTester histogram_tester;
-  UserClassifier* user_classifier = CreateUserClassifier();
-
-  user_classifier->OnEvent(metric);
-  EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name), SizeIs(1));
-}
-
 TEST_P(UserClassifierMetricTest, ShouldConvergeTowardsPattern) {
-  UserClassifier::Metric metric = GetParam().first;
+  UserClassifier::Metric metric = GetParam();
   UserClassifier* user_classifier = CreateUserClassifier();
 
   // Have the pattern of an event every five hours and start changing it towards
@@ -199,7 +187,7 @@
 }
 
 TEST_P(UserClassifierMetricTest, ShouldIgnoreSubsequentEventsForHalfAnHour) {
-  UserClassifier::Metric metric = GetParam().first;
+  UserClassifier::Metric metric = GetParam();
   UserClassifier* user_classifier = CreateUserClassifier();
 
   // The initial event
@@ -220,7 +208,7 @@
 
 TEST_P(UserClassifierMetricTest,
        ShouldIgnoreSubsequentEventsWithIncreasedLimit) {
-  UserClassifier::Metric metric = GetParam().first;
+  UserClassifier::Metric metric = GetParam();
   // Increase the min_hours to 1.0, i.e. 60 minutes.
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
@@ -244,7 +232,7 @@
 }
 
 TEST_P(UserClassifierMetricTest, ShouldCapDelayBetweenEvents) {
-  UserClassifier::Metric metric = GetParam().first;
+  UserClassifier::Metric metric = GetParam();
   UserClassifier* user_classifier = CreateUserClassifier();
 
   // The initial event
@@ -267,7 +255,7 @@
 
 TEST_P(UserClassifierMetricTest,
        ShouldCapDelayBetweenEventsWithDecreasedLimit) {
-  UserClassifier::Metric metric = GetParam().first;
+  UserClassifier::Metric metric = GetParam();
   // Decrease the max_hours to 72, i.e. 3 days.
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
@@ -295,15 +283,9 @@
 INSTANTIATE_TEST_SUITE_P(
     NTP,
     UserClassifierMetricTest,
-    testing::Values(
-        std::make_pair(UserClassifier::Metric::NTP_OPENED,
-                       "NewTabPage.UserClassifier.AverageHoursToOpenNTP"),
-        std::make_pair(
-            UserClassifier::Metric::SUGGESTIONS_SHOWN,
-            "NewTabPage.UserClassifier.AverageHoursToShowSuggestions"),
-        std::make_pair(
-            UserClassifier::Metric::SUGGESTIONS_USED,
-            "NewTabPage.UserClassifier.AverageHoursToUseSuggestions")));
+    testing::Values(UserClassifier::Metric::NTP_OPENED,
+                    UserClassifier::Metric::SUGGESTIONS_SHOWN,
+                    UserClassifier::Metric::SUGGESTIONS_USED));
 
 }  // namespace
 }  // namespace ntp_snippets
diff --git a/components/omnibox/browser/actions/history_clusters_action.cc b/components/omnibox/browser/actions/history_clusters_action.cc
index f256e83..5523153 100644
--- a/components/omnibox/browser/actions/history_clusters_action.cc
+++ b/components/omnibox/browser/actions/history_clusters_action.cc
@@ -168,8 +168,8 @@
   OmniboxAction::Execute(context);
 }
 
-int32_t HistoryClustersAction::GetID() const {
-  return static_cast<int32_t>(OmniboxActionId::HISTORY_CLUSTERS);
+OmniboxActionId HistoryClustersAction::ActionId() const {
+  return OmniboxActionId::HISTORY_CLUSTERS;
 }
 
 #if defined(SUPPORT_PEDALS_VECTOR_ICONS)
@@ -186,7 +186,7 @@
 
 void HistoryClustersAction::CreateOrUpdateJavaObject(const std::string& query) {
   j_omnibox_action_.Reset(BuildHistoryClustersAction(
-      GetID(), strings_.hint, strings_.suggestion_contents,
+      strings_.hint, strings_.suggestion_contents,
       strings_.accessibility_suffix, strings_.accessibility_hint, url_, query));
 }
 #endif
diff --git a/components/omnibox/browser/actions/history_clusters_action.h b/components/omnibox/browser/actions/history_clusters_action.h
index 41a3c5d..025b48d 100644
--- a/components/omnibox/browser/actions/history_clusters_action.h
+++ b/components/omnibox/browser/actions/history_clusters_action.h
@@ -52,7 +52,7 @@
 
   void RecordActionShown(size_t position, bool executed) const override;
   void Execute(ExecutionContext& context) const override;
-  int32_t GetID() const override;
+  OmniboxActionId ActionId() const override;
 #if defined(SUPPORT_PEDALS_VECTOR_ICONS)
   const gfx::VectorIcon& GetVectorIcon() const override;
 #endif
diff --git a/components/omnibox/browser/actions/history_clusters_action_unittest.cc b/components/omnibox/browser/actions/history_clusters_action_unittest.cc
index e1aae3f..aea6f37c 100644
--- a/components/omnibox/browser/actions/history_clusters_action_unittest.cc
+++ b/components/omnibox/browser/actions/history_clusters_action_unittest.cc
@@ -113,8 +113,8 @@
     for (size_t i = 0; i < matches_data.size(); ++i) {
       bool has_history_clusters_action =
           result.match_at(i)->GetPrimaryAction() &&
-          result.match_at(i)->GetPrimaryAction()->GetID() ==
-              static_cast<int32_t>(OmniboxActionId::HISTORY_CLUSTERS);
+          result.match_at(i)->GetPrimaryAction()->ActionId() ==
+              OmniboxActionId::HISTORY_CLUSTERS;
       EXPECT_EQ(has_history_clusters_action,
                 matches_data[i].expect_history_clusters_action);
     }
diff --git a/components/omnibox/browser/actions/omnibox_action.cc b/components/omnibox/browser/actions/omnibox_action.cc
index 33825542..7cd9693 100644
--- a/components/omnibox/browser/actions/omnibox_action.cc
+++ b/components/omnibox/browser/actions/omnibox_action.cc
@@ -105,8 +105,8 @@
   return total;
 }
 
-int32_t OmniboxAction::GetID() const {
-  return 0;
+OmniboxActionId OmniboxAction::ActionId() const {
+  return OmniboxActionId::UNKNOWN;
 }
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/components/omnibox/browser/actions/omnibox_action.h b/components/omnibox/browser/actions/omnibox_action.h
index 295de70..e3fa9ba 100644
--- a/components/omnibox/browser/actions/omnibox_action.h
+++ b/components/omnibox/browser/actions/omnibox_action.h
@@ -12,6 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/omnibox/browser/actions/omnibox_action_concepts.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/omnibox/browser/buildflags.h"
 #include "components/search_engines/template_url.h"
@@ -164,7 +165,7 @@
   virtual size_t EstimateMemoryUsage() const;
 
   // Returns an ID used to identify the action.
-  virtual int32_t GetID() const;
+  virtual OmniboxActionId ActionId() const;
 
 #if BUILDFLAG(IS_ANDROID)
   virtual base::android::ScopedJavaGlobalRef<jobject> GetJavaObject() const;
diff --git a/components/omnibox/browser/actions/omnibox_action_concepts.h b/components/omnibox/browser/actions/omnibox_action_concepts.h
index 1c23ba7..ad0a965 100644
--- a/components/omnibox/browser/actions/omnibox_action_concepts.h
+++ b/components/omnibox/browser/actions/omnibox_action_concepts.h
@@ -8,24 +8,19 @@
 #include "components/omnibox/browser/actions/omnibox_pedal_concepts.h"
 
 // Unique identifiers for actions that aren't pedals, e.g. the history clusters
-// action. Do not remove or reuse values. The values here must remain disjoint
-// with the OmniboxPedalId enum so they start at 10000.
+// action. Do not remove or reuse values.
 //
 // Automatically generate a corresponding Java enum:
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.omnibox.action
 // GENERATED_JAVA_CLASS_NAME_OVERRIDE: OmniboxActionType
 enum class OmniboxActionId {
-  FIRST = 10000,
-  HISTORY_CLUSTERS = 10001,
+  UNKNOWN = 0,
+  PEDAL,
+  HISTORY_CLUSTERS,
 
-  // Last value, used to track the upper bound. This intentionally does not have
-  // an assigned value to ensure that it's always 1 greater than the last
-  // assigned value.
+  // Keep as a last item in the list, with ID one larger than the last valid
+  // Action Id.
   LAST
 };
 
-static_assert(static_cast<int32_t>(OmniboxActionId::FIRST) >
-                  static_cast<int32_t>(OmniboxPedalId::TOTAL_COUNT),
-              "OmniboxPedalId and OmniboxActionId must remain disjoint");
-
 #endif  // COMPONENTS_OMNIBOX_BROWSER_ACTIONS_OMNIBOX_ACTION_CONCEPTS_H_
diff --git a/components/omnibox/browser/actions/omnibox_pedal.cc b/components/omnibox/browser/actions/omnibox_pedal.cc
index 2ef5e754..aef3b0ee 100644
--- a/components/omnibox/browser/actions/omnibox_pedal.cc
+++ b/components/omnibox/browser/actions/omnibox_pedal.cc
@@ -236,6 +236,14 @@
 #endif
 }
 
+/* static */ const OmniboxPedal* OmniboxPedal::FromAction(
+    const OmniboxAction* action) {
+  if (action && action->ActionId() == OmniboxActionId::PEDAL) {
+    return static_cast<const OmniboxPedal*>(action);
+  }
+  return nullptr;
+}
+
 OmniboxPedal::~OmniboxPedal() = default;
 
 void OmniboxPedal::OnLoaded() {
@@ -275,7 +283,7 @@
 }
 
 OmniboxPedalId OmniboxPedal::GetMetricsId() const {
-  return id();
+  return PedalId();
 }
 
 bool OmniboxPedal::IsConceptMatch(TokenSequence& match_sequence) const {
@@ -312,8 +320,8 @@
   return total;
 }
 
-int32_t OmniboxPedal::GetID() const {
-  return static_cast<int32_t>(id());
+OmniboxActionId OmniboxPedal::ActionId() const {
+  return OmniboxActionId::PEDAL;
 }
 
 #if BUILDFLAG(IS_ANDROID)
@@ -324,7 +332,7 @@
 
 void OmniboxPedal::CreateOrUpdateJavaObject() {
   j_omnibox_action_.Reset(BuildOmniboxPedal(
-      GetID(), strings_.hint, strings_.suggestion_contents,
+      PedalId(), strings_.hint, strings_.suggestion_contents,
       strings_.accessibility_suffix, strings_.accessibility_hint, url_));
 }
 #endif
diff --git a/components/omnibox/browser/actions/omnibox_pedal.h b/components/omnibox/browser/actions/omnibox_pedal.h
index e6c8f08d..b3cfcc2 100644
--- a/components/omnibox/browser/actions/omnibox_pedal.h
+++ b/components/omnibox/browser/actions/omnibox_pedal.h
@@ -176,6 +176,10 @@
 
   OmniboxPedal(OmniboxPedalId id, LabelStrings strings, GURL url);
 
+  // Return an OmniboxPedal instance if the supplied OmniboxAction instance
+  // represents one.
+  static const OmniboxPedal* FromAction(const OmniboxAction* action);
+
   // Called after the OmniboxPedalProvider finishes loading all pedals data.
   // This can be used to override implementation bits based on flags, etc.
   virtual void OnLoaded();
@@ -208,7 +212,7 @@
   virtual std::vector<SynonymGroupSpec> SpecifySynonymGroups(
       bool locale_is_english) const;
 
-  OmniboxPedalId id() const { return id_; }
+  OmniboxPedalId PedalId() const { return id_; }
 
   // Sometimes pedals report different IDs for metrics, either to enable
   // feature discrimination (e.g. incognito mode) or to unify metrics
@@ -228,7 +232,7 @@
   const gfx::VectorIcon& GetVectorIcon() const override;
 #endif
   size_t EstimateMemoryUsage() const override;
-  int32_t GetID() const override;
+  OmniboxActionId ActionId() const override;
 
 #if BUILDFLAG(IS_ANDROID)
   base::android::ScopedJavaGlobalRef<jobject> GetJavaObject() const override;
diff --git a/components/omnibox/browser/actions/omnibox_pedal_jni_wrapper.cc b/components/omnibox/browser/actions/omnibox_pedal_jni_wrapper.cc
index 3681b5a2..6ae5bcd 100644
--- a/components/omnibox/browser/actions/omnibox_pedal_jni_wrapper.cc
+++ b/components/omnibox/browser/actions/omnibox_pedal_jni_wrapper.cc
@@ -14,7 +14,7 @@
 #include "url/android/gurl_android.h"
 
 base::android::ScopedJavaGlobalRef<jobject> BuildOmniboxPedal(
-    int id,
+    OmniboxPedalId pedal_id,
     std::u16string hint,
     std::u16string suggestion_contents,
     std::u16string accessibility_suffix,
@@ -22,7 +22,8 @@
     GURL url) {
   JNIEnv* env = base::android::AttachCurrentThread();
   return base::android::ScopedJavaGlobalRef(Java_OmniboxPedal_build(
-      env, id, base::android::ConvertUTF16ToJavaString(env, hint),
+      env, static_cast<int32_t>(pedal_id),
+      base::android::ConvertUTF16ToJavaString(env, hint),
       base::android::ConvertUTF16ToJavaString(env, suggestion_contents),
       base::android::ConvertUTF16ToJavaString(env, accessibility_suffix),
       base::android::ConvertUTF16ToJavaString(env, accessibility_hint),
@@ -30,7 +31,6 @@
 }
 
 base::android::ScopedJavaGlobalRef<jobject> BuildHistoryClustersAction(
-    int id,
     std::u16string hint,
     std::u16string suggestion_contents,
     std::u16string accessibility_suffix,
@@ -39,7 +39,7 @@
     std::string query) {
   JNIEnv* env = base::android::AttachCurrentThread();
   return base::android::ScopedJavaGlobalRef(Java_HistoryClustersAction_build(
-      env, id, base::android::ConvertUTF16ToJavaString(env, hint),
+      env, base::android::ConvertUTF16ToJavaString(env, hint),
       base::android::ConvertUTF16ToJavaString(env, suggestion_contents),
       base::android::ConvertUTF16ToJavaString(env, accessibility_suffix),
       base::android::ConvertUTF16ToJavaString(env, accessibility_hint),
diff --git a/components/omnibox/browser/actions/omnibox_pedal_jni_wrapper.h b/components/omnibox/browser/actions/omnibox_pedal_jni_wrapper.h
index 7fdb62a2..998949c 100644
--- a/components/omnibox/browser/actions/omnibox_pedal_jni_wrapper.h
+++ b/components/omnibox/browser/actions/omnibox_pedal_jni_wrapper.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_OMNIBOX_BROWSER_ACTIONS_OMNIBOX_PEDAL_JNI_WRAPPER_H_
 
 base::android::ScopedJavaGlobalRef<jobject> BuildOmniboxPedal(
-    int id,
+    OmniboxPedalId pedal_id,
     std::u16string hint,
     std::u16string suggestion_contents,
     std::u16string accessibility_suffix,
@@ -14,7 +14,6 @@
     GURL url);
 
 base::android::ScopedJavaGlobalRef<jobject> BuildHistoryClustersAction(
-    int id,
     std::u16string hint,
     std::u16string suggestion_contents,
     std::u16string accessibility_suffix,
diff --git a/components/omnibox/browser/actions/omnibox_pedal_provider_unittest.cc b/components/omnibox/browser/actions/omnibox_pedal_provider_unittest.cc
index 663db33..3d3a13e 100644
--- a/components/omnibox/browser/actions/omnibox_pedal_provider_unittest.cc
+++ b/components/omnibox/browser/actions/omnibox_pedal_provider_unittest.cc
@@ -26,7 +26,8 @@
   MockAutocompleteProviderClient client;
   std::unordered_map<OmniboxPedalId, scoped_refptr<OmniboxPedal>> pedals;
   const auto add = [&](OmniboxPedal* pedal) {
-    pedals.insert(std::make_pair(pedal->id(), base::WrapRefCounted(pedal)));
+    pedals.insert(
+        std::make_pair(pedal->PedalId(), base::WrapRefCounted(pedal)));
   };
   add(new TestOmniboxPedalClearBrowsingData());
   client.set_pedal_provider(
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/action/HistoryClustersAction.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/action/HistoryClustersAction.java
index 2c21d50..0953413 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/action/HistoryClustersAction.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/action/HistoryClustersAction.java
@@ -8,6 +8,8 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.omnibox.action.OmniboxActionType;
+import org.chromium.chrome.browser.omnibox.action.OmniboxPedalType;
 import org.chromium.url.GURL;
 
 /**
@@ -17,10 +19,11 @@
 public class HistoryClustersAction extends OmniboxPedal {
     private final String mQuery;
 
-    public HistoryClustersAction(int id, @NonNull String hint, @NonNull String suggestionContents,
+    public HistoryClustersAction(@NonNull String hint, @NonNull String suggestionContents,
             @NonNull String accessibilitySuffix, @NonNull String accessibilityHint,
             @Nullable GURL url, @NonNull String query) {
-        super(id, hint, suggestionContents, accessibilitySuffix, accessibilityHint, url);
+        super(OmniboxActionType.HISTORY_CLUSTERS, OmniboxPedalType.NONE, hint, suggestionContents,
+                accessibilitySuffix, accessibilityHint, url);
         mQuery = query;
     }
 
@@ -29,10 +32,10 @@
     }
 
     @CalledByNative
-    private static HistoryClustersAction build(int id, @NonNull String hint,
+    private static HistoryClustersAction build(@NonNull String hint,
             @NonNull String suggestionContents, @NonNull String accessibilitySuffix,
             @NonNull String accessibilityHint, @Nullable GURL url, @NonNull String query) {
         return new HistoryClustersAction(
-                id, hint, suggestionContents, accessibilitySuffix, accessibilityHint, url, query);
+                hint, suggestionContents, accessibilitySuffix, accessibilityHint, url, query);
     }
 }
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/action/OmniboxPedal.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/action/OmniboxPedal.java
index a2e495d..3c2cfc4 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/action/OmniboxPedal.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/action/OmniboxPedal.java
@@ -17,17 +17,20 @@
  * please check on OmniboxAction class definition on native side.
  */
 public class OmniboxPedal {
-    private final @OmniboxPedalType int mId;
+    private final @OmniboxActionType int mActionId;
+    private final @OmniboxPedalType int mPedalId;
     private final @NonNull String mHint;
     private final @NonNull String mSuggestionContents;
     private final @NonNull String mAccessibilitySuffix;
     private final @NonNull String mAccessibilityHint;
     private final @Nullable GURL mUrl;
 
-    public OmniboxPedal(@OmniboxPedalType int id, @NonNull String hint,
-            @NonNull String suggestionContents, @NonNull String accessibilitySuffix,
-            @NonNull String accessibilityHint, @Nullable GURL url) {
-        mId = id;
+    public OmniboxPedal(@OmniboxActionType int actionId, @OmniboxPedalType int pedalId,
+            @NonNull String hint, @NonNull String suggestionContents,
+            @NonNull String accessibilitySuffix, @NonNull String accessibilityHint,
+            @Nullable GURL url) {
+        mActionId = actionId;
+        mPedalId = pedalId;
         mHint = hint;
         mSuggestionContents = suggestionContents;
         mAccessibilitySuffix = accessibilitySuffix;
@@ -35,28 +38,18 @@
         mUrl = url;
     }
 
-    public boolean hasPedalId() {
-        return (mId >= OmniboxPedalType.NONE) && mId < (OmniboxPedalType.TOTAL_COUNT);
-    }
-
-    public boolean hasActionId() {
-        return (mId >= OmniboxActionType.FIRST) && mId < (OmniboxActionType.LAST);
-    }
-
     /**
      * @return an ID used to identify the underlying pedal.
      */
-    public @OmniboxPedalType int getPedalID() {
-        assert hasPedalId();
-        return mId;
+    public @OmniboxPedalType int getPedalId() {
+        return mPedalId;
     }
 
     /**
      * @return an ID used to identify the underlying action.
      */
-    public @OmniboxActionType int getActionID() {
-        assert hasActionId();
-        return mId;
+    public @OmniboxActionType int getActionId() {
+        return mActionId;
     }
 
     /**
@@ -98,7 +91,7 @@
     private static OmniboxPedal build(int id, @NonNull String hint,
             @NonNull String suggestionContents, @NonNull String accessibilitySuffix,
             @NonNull String accessibilityHint, @Nullable GURL url) {
-        return new OmniboxPedal(
-                id, hint, suggestionContents, accessibilitySuffix, accessibilityHint, url);
+        return new OmniboxPedal(OmniboxActionType.PEDAL, id, hint, suggestionContents,
+                accessibilitySuffix, accessibilityHint, url);
     }
-}
\ No newline at end of file
+}
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index 820eaf8a..14ddc32 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -2391,7 +2391,8 @@
   FakeAutocompleteProviderClient client;
   std::unordered_map<OmniboxPedalId, scoped_refptr<OmniboxPedal>> pedals;
   const auto add = [&](OmniboxPedal* pedal) {
-    pedals.insert(std::make_pair(pedal->id(), base::WrapRefCounted(pedal)));
+    pedals.insert(
+        std::make_pair(pedal->PedalId(), base::WrapRefCounted(pedal)));
   };
   add(new TestOmniboxPedalClearBrowsingData());
   client.set_pedal_provider(
@@ -2455,8 +2456,8 @@
               return true;
             }));
   ASSERT_NE(nullptr, result.begin()->GetActionWhere([](const auto& action) {
-    return action->GetID() ==
-           static_cast<int32_t>(OmniboxPedalId::CLEAR_BROWSING_DATA);
+    const auto* pedal = OmniboxPedal::FromAction(action.get());
+    return pedal && pedal->PedalId() == OmniboxPedalId::CLEAR_BROWSING_DATA;
   }));
 }
 
diff --git a/components/previous_session_info/README b/components/previous_session_info/README
deleted file mode 100644
index ea885d60c..0000000
--- a/components/previous_session_info/README
+++ /dev/null
@@ -1,3 +0,0 @@
-# PreviousSessionInfo
-
-PreviousSessionInfo is a component to allow using this code in other components code on iOS.
diff --git a/components/previous_session_info/README.md b/components/previous_session_info/README.md
new file mode 100644
index 0000000..e13319b
--- /dev/null
+++ b/components/previous_session_info/README.md
@@ -0,0 +1,8 @@
+# PreviousSessionInfo
+
+This component exposes a class `PreviousSessionInfo` which holds information
+about the last application session and persists information about the current
+session.
+
+PreviousSessionInfo is implemented as a component to allow using this code in
+other components code on iOS.
diff --git a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.cc b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.cc
index d7cddcf..e76de70 100644
--- a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.cc
+++ b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.cc
@@ -71,25 +71,43 @@
 class ObliviousHttpClient : public network::mojom::ObliviousHttpClient {
  public:
   using OnCompletedCallback =
-      base::OnceCallback<void(const absl::optional<std::string>&, int)>;
+      base::OnceCallback<void(const absl::optional<std::string>&, int, int)>;
 
   explicit ObliviousHttpClient(OnCompletedCallback callback)
       : callback_(std::move(callback)) {}
 
   ~ObliviousHttpClient() override {
     if (!called_) {
-      std::move(callback_).Run(absl::nullopt, net::ERR_FAILED);
+      std::move(callback_).Run(absl::nullopt, net::ERR_FAILED,
+                               /*response_code=*/0);
     }
   }
 
-  void OnCompleted(const absl::optional<std::string>& response,
-                   int net_error) override {
+  void OnCompleted(
+      network::mojom::ObliviousHttpCompletionResultPtr status) override {
     if (called_) {
       mojo::ReportBadMessage("OnCompleted called more than once");
       return;
     }
     called_ = true;
-    std::move(callback_).Run(response, net_error);
+    if (status->is_net_error()) {
+      std::move(callback_).Run(absl::nullopt, status->get_net_error(),
+                               /*response_code=*/0);
+    } else if (status->is_outer_response_error_code()) {
+      std::move(callback_).Run(absl::nullopt,
+                               net::ERR_HTTP_RESPONSE_CODE_FAILURE,
+                               status->get_outer_response_error_code());
+    } else {
+      DCHECK(status->is_inner_response());
+      if (status->get_inner_response()->response_code != net::HTTP_OK) {
+        std::move(callback_).Run(absl::nullopt,
+                                 net::ERR_HTTP_RESPONSE_CODE_FAILURE,
+                                 status->get_inner_response()->response_code);
+      } else {
+        std::move(callback_).Run(status->get_inner_response()->response_body,
+                                 net::OK, net::HTTP_OK);
+      }
+    }
   }
 
  private:
@@ -361,19 +379,17 @@
     HPRTLookupResponseCallback response_callback,
     SBThreatType locally_cached_results_threat_type,
     const absl::optional<std::string>& response_body,
-    int net_error) {
+    int net_error,
+    int response_code) {
   // TODO(crbug.com/1407283): Notify ohttp_key_service_ if the error is key
   // related.
   auto response_body_ptr =
       std::make_unique<std::string>(response_body.value_or(""));
-  // TODO(crbug.com/1407283): Set response_code when the mojo interface exposes
-  // response_code.
   OnURLLoaderComplete(
       url, std::move(hash_prefixes_in_request), std::move(result_full_hashes),
       request_start_time, std::move(response_callback_task_runner),
       std::move(response_callback), locally_cached_results_threat_type,
-      std::move(response_body_ptr), net_error,
-      /*response_code=*/net::HTTP_OK);
+      std::move(response_body_ptr), net_error, response_code);
 }
 
 void HashRealTimeService::OnDirectURLLoaderComplete(
@@ -417,7 +433,6 @@
     int net_error,
     int response_code) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
   base::UmaHistogramTimes("SafeBrowsing.HPRT.Network.Time",
                           base::TimeTicks::Now() - request_start_time);
   RecordHttpResponseOrErrorCode("SafeBrowsing.HPRT.Network.Result", net_error,
@@ -544,7 +559,8 @@
     return base::unexpected(OperationResult::kParseError);
   } else if (ErrorIsRetriable(net_error, response_code)) {
     return base::unexpected(OperationResult::kRetriableError);
-  } else if (net_error != net::OK) {
+  } else if (net_error != net::OK &&
+             net_error != net::ERR_HTTP_RESPONSE_CODE_FAILURE) {
     return base::unexpected(OperationResult::kNetworkError);
   } else if (response_code != net::HTTP_OK) {
     return base::unexpected(OperationResult::kHttpError);
diff --git a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.h b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.h
index c520f064..8c61832 100644
--- a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.h
+++ b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.h
@@ -86,7 +86,13 @@
 
  private:
   friend class HashRealTimeServiceTest;
-  FRIEND_TEST_ALL_PREFIXES(HashRealTimeServiceTest, TestLookupFailure_Error);
+  FRIEND_TEST_ALL_PREFIXES(HashRealTimeServiceTest, TestLookupFailure_NetError);
+  FRIEND_TEST_ALL_PREFIXES(HashRealTimeServiceTest,
+                           TestLookupFailure_NetErrorHttpCodeFailure);
+  FRIEND_TEST_ALL_PREFIXES(HashRealTimeServiceTest,
+                           TestLookupFailure_OuterResponseCodeError);
+  FRIEND_TEST_ALL_PREFIXES(HashRealTimeServiceTest,
+                           TestLookupFailure_InnerResponseCodeError);
   FRIEND_TEST_ALL_PREFIXES(HashRealTimeServiceTest,
                            TestLookupFailure_ParseResponse);
   FRIEND_TEST_ALL_PREFIXES(HashRealTimeServiceTest,
@@ -157,8 +163,8 @@
 
   // Callback for requests sent via OHTTP. Most parameters are used by
   // |OnURLLoaderComplete|, see the description above |OnURLLoaderComplete| for
-  // details. |response_body| and |net_error| are returned from the OHTTP
-  // client.
+  // details. |response_body|, |net_error| and |response_code| are returned from
+  // the OHTTP client.
   void OnOhttpComplete(
       const GURL& url,
       const std::vector<std::string>& hash_prefixes_in_request,
@@ -168,7 +174,8 @@
       HPRTLookupResponseCallback response_callback,
       SBThreatType locally_cached_results_threat_type,
       const absl::optional<std::string>& response_body,
-      int net_error);
+      int net_error,
+      int response_code);
 
   // Callback for requests sent directly to the Safe Browsing server. Most
   // parameters are used by |OnURLLoaderComplete|, see the description above
diff --git a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service_unittest.cc b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service_unittest.cc
index fa7c52e..e210bfb0 100644
--- a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service_unittest.cc
+++ b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service_unittest.cc
@@ -58,17 +58,43 @@
     }
     auto it = responses_.find(resource_url);
     ASSERT_TRUE(it != responses_.end());
-    remote_->OnCompleted(responses_[resource_url].body,
-                         responses_[resource_url].net_error);
+    if (responses_[resource_url].net_error.has_value()) {
+      auto completion_result =
+          network::mojom::ObliviousHttpCompletionResult::NewNetError(
+              responses_[resource_url].net_error.value());
+      remote_->OnCompleted(std::move(completion_result));
+    } else if (responses_[resource_url].outer_response_error_code.has_value()) {
+      auto completion_result = network::mojom::ObliviousHttpCompletionResult::
+          NewOuterResponseErrorCode(
+              responses_[resource_url].outer_response_error_code.value());
+      remote_->OnCompleted(std::move(completion_result));
+    } else {
+      auto response = network::mojom::ObliviousHttpResponse::New();
+      response->response_body = std::move(responses_[resource_url].body);
+      if (responses_[resource_url].inner_response_code.has_value()) {
+        response->response_code =
+            responses_[resource_url].inner_response_code.value();
+      } else {
+        response->response_code = net::HTTP_OK;
+      }
+      auto completion_result =
+          network::mojom::ObliviousHttpCompletionResult::NewInnerResponse(
+              std::move(response));
+      remote_->OnCompleted(std::move(completion_result));
+    }
     remote_.reset();
   }
 
   void AddResponse(std::string resource_url,
-                   absl::optional<std::string> body,
-                   int net_error) {
+                   std::string body,
+                   absl::optional<int> net_error,
+                   absl::optional<int> outer_response_error_code,
+                   absl::optional<int> inner_response_code) {
     Response response;
     response.body = body;
     response.net_error = net_error;
+    response.outer_response_error_code = outer_response_error_code;
+    response.inner_response_code = inner_response_code;
     responses_[GURL(resource_url)] = std::move(response);
   }
 
@@ -82,8 +108,10 @@
 
  private:
   struct Response {
-    absl::optional<std::string> body;
-    int net_error;
+    std::string body;
+    absl::optional<int> net_error;
+    absl::optional<int> outer_response_error_code;
+    absl::optional<int> inner_response_code;
   };
 
   std::map<GURL, Response> responses_;
@@ -213,7 +241,10 @@
       const std::unique_ptr<V5::SearchHashesResponse>& response) {
     std::string expected_response_str;
     response->SerializeToString(&expected_response_str);
-    network_context_.AddResponse(request_url, expected_response_str, net::OK);
+    network_context_.AddResponse(request_url, expected_response_str,
+                                 /*net_error=*/absl::nullopt,
+                                 /*outer_response_error_code=*/absl::nullopt,
+                                 /*inner_response_code=*/absl::nullopt);
   }
   void SetUpLookupResponse(const std::string& request_url,
                            const std::vector<V5::FullHash>& full_hashes) {
@@ -387,13 +418,16 @@
   }
   // Starts a lookup on |url| that is expected to fail. The simulated server
   // response body can be specified either by |response_full_hashes| or by
-  // |custom_response|. |net_error| represents the simulated OHTTP handler error
-  // code. Confirms that the lookup fails.
+  // |custom_response|. |net_error|, |outer_response_error_code| and
+  // |inner_response_code| represent the simulated OHTTP handler error.
+  // Confirms that the lookup fails.
   void RunRequestFailureTest(
       const GURL& url,
       const absl::optional<std::vector<V5::FullHash>>& response_full_hashes,
-      const absl::optional<std::string>& custom_response,
-      net::Error net_error,
+      const std::string& custom_response,
+      absl::optional<net::Error> net_error,
+      absl::optional<int> outer_response_error_code,
+      absl::optional<int> inner_response_code,
       int expected_prefix_count,
       int expected_network_result,
       HashRealTimeService::OperationResult expected_operation_result) {
@@ -409,11 +443,10 @@
       SetUpLookupResponse(
           /*request_url=*/expected_url,
           /*full_hashes=*/response_full_hashes.value());
-    } else if (custom_response.has_value()) {
-      network_context_.AddResponse(expected_url, custom_response.value(),
-                                   net_error);
     } else {
-      network_context_.AddResponse(expected_url, absl::nullopt, net_error);
+      network_context_.AddResponse(expected_url, custom_response, net_error,
+                                   outer_response_error_code,
+                                   inner_response_code);
     }
 
     // Start lookup.
@@ -521,7 +554,9 @@
       request->add_hash_prefixes(hash_prefix);
     }
     std::string expected_url = GetExpectedRequestUrl(request);
-    network_context_.AddResponse(expected_url, "", net::ERR_FAILED);
+    network_context_.AddResponse(expected_url, "", net::ERR_FAILED,
+                                 /*outer_response_error_code=*/absl::nullopt,
+                                 /*inner_response_code=*/absl::nullopt);
 
     // Start lookup.
     base::MockCallback<HPRTLookupResponseCallback> response_callback;
@@ -921,22 +956,64 @@
       /*expected_found_unmatched_full_hashes=*/false);
 }
 
-TEST_F(HashRealTimeServiceTest, TestLookupFailure_Error) {
+TEST_F(HashRealTimeServiceTest, TestLookupFailure_NetError) {
   GURL url = GURL("https://example.test");
   RunRequestFailureTest(
       /*url=*/url, /*response_full_hashes=*/absl::nullopt,
-      /*custom_response=*/absl::nullopt,
-      /*net_error=*/net::ERR_FAILED, /*expected_prefix_count=*/1,
+      /*custom_response=*/"",
+      /*net_error=*/net::ERR_FAILED,
+      /*outer_response_error_code=*/absl::nullopt,
+      /*inner_response_code=*/absl::nullopt,
+      /*expected_prefix_count=*/1,
       /*expected_network_result=*/net::ERR_FAILED,
       /*expected_operation_result=*/
       HashRealTimeService::OperationResult::kNetworkError);
 }
+TEST_F(HashRealTimeServiceTest, TestLookupFailure_NetErrorHttpCodeFailure) {
+  GURL url = GURL("https://example.test");
+  RunRequestFailureTest(
+      /*url=*/url, /*response_full_hashes=*/absl::nullopt,
+      /*custom_response=*/"",
+      /*net_error=*/net::ERR_HTTP_RESPONSE_CODE_FAILURE,
+      /*outer_response_error_code=*/absl::nullopt,
+      /*inner_response_code=*/absl::nullopt,
+      /*expected_prefix_count=*/1,
+      /*expected_network_result=*/0,
+      /*expected_operation_result=*/
+      HashRealTimeService::OperationResult::kHttpError);
+}
+TEST_F(HashRealTimeServiceTest, TestLookupFailure_OuterResponseCodeError) {
+  GURL url = GURL("https://example.test");
+  RunRequestFailureTest(
+      /*url=*/url, /*response_full_hashes=*/absl::nullopt,
+      /*custom_response=*/"",
+      /*net_error=*/absl::nullopt,
+      /*outer_response_error_code=*/net::HTTP_NOT_FOUND,
+      /*inner_response_code=*/absl::nullopt,
+      /*expected_prefix_count=*/1,
+      /*expected_network_result=*/net::HTTP_NOT_FOUND,
+      /*expected_operation_result=*/
+      HashRealTimeService::OperationResult::kHttpError);
+}
+TEST_F(HashRealTimeServiceTest, TestLookupFailure_InnerResponseCodeError) {
+  GURL url = GURL("https://example.test");
+  RunRequestFailureTest(
+      /*url=*/url, /*response_full_hashes=*/absl::nullopt,
+      /*custom_response=*/"",
+      /*net_error=*/absl::nullopt, /*outer_response_error_code=*/absl::nullopt,
+      /*inner_response_code=*/net::HTTP_UNAUTHORIZED,
+      /*expected_prefix_count=*/1,
+      /*expected_network_result=*/net::HTTP_UNAUTHORIZED,
+      /*expected_operation_result=*/
+      HashRealTimeService::OperationResult::kHttpError);
+}
 TEST_F(HashRealTimeServiceTest, TestLookupFailure_ParseResponse) {
   GURL url = GURL("https://example.test");
   RunRequestFailureTest(
       /*url=*/url, /*response_full_hashes=*/absl::nullopt,
       /*custom_response=*/"howdy",
-      /*net_error=*/net::OK, /*expected_prefix_count=*/1,
+      /*net_error=*/absl::nullopt, /*outer_response_error_code=*/absl::nullopt,
+      /*inner_response_code=*/absl::nullopt, /*expected_prefix_count=*/1,
       /*expected_network_result=*/net::HTTP_OK,
       /*expected_operation_result=*/
       HashRealTimeService::OperationResult::kParseError);
@@ -948,8 +1025,9 @@
       /*url=*/url, /*response_full_hashes=*/
       absl::optional<std::vector<V5::FullHash>>({CreateFullHashProto(
           {V5::ThreatType::SOCIAL_ENGINEERING}, short_full_hash)}),
-      /*custom_response=*/absl::nullopt,
-      /*net_error=*/net::OK, /*expected_prefix_count=*/1,
+      /*custom_response=*/"",
+      /*net_error=*/absl::nullopt, /*outer_response_error_code=*/absl::nullopt,
+      /*inner_response_code=*/absl::nullopt, /*expected_prefix_count=*/1,
       /*expected_network_result=*/net::HTTP_OK,
       /*expected_operation_result=*/
       HashRealTimeService::OperationResult::kIncorrectFullHashLengthError);
@@ -963,7 +1041,8 @@
   RunRequestFailureTest(
       /*url=*/url, /*response_full_hashes=*/{},
       /*custom_response=*/response_str,
-      /*net_error=*/net::OK,
+      /*net_error=*/absl::nullopt, /*outer_response_error_code=*/absl::nullopt,
+      /*inner_response_code=*/absl::nullopt,
       /*expected_prefix_count=*/1,
       /*expected_network_result=*/net::HTTP_OK,
       /*expected_operation_result=*/
diff --git a/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc b/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
index 5317738..15e5bd5a 100644
--- a/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
@@ -127,21 +127,11 @@
     if (IsBypassEventType(event_type)) {
       int bypass_count = GetEventCountSince(user_state, event_type,
                                             base::Time::Now() - base::Days(28));
-      base::UmaHistogramCounts100("SafeBrowsing.Daily.BypassCountLast28Days." +
-                                      GetUserStateMetricSuffix(user_state) +
-                                      "." +
-                                      GetEventTypeMetricSuffix(event_type),
-                                  bypass_count);
       total_bypass_count += bypass_count;
     }
     if (IsSecuritySensitiveEventType(event_type)) {
       int security_sensitive_event_count = GetEventCountSince(
           user_state, event_type, base::Time::Now() - base::Days(28));
-      base::UmaHistogramCounts100(
-          "SafeBrowsing.Daily.SecuritySensitiveCountLast28Days." +
-              GetUserStateMetricSuffix(user_state) + "." +
-              GetEventTypeMetricSuffix(event_type),
-          security_sensitive_event_count);
       total_security_sensitive_event_count += security_sensitive_event_count;
     }
   }
@@ -160,13 +150,6 @@
                               prefs::kSafeBrowsingEventTimestamps);
   base::Value::Dict& mutable_state_dict = update.Get();
 
-  // Histogram to check whether prefs::kSafeBrowsingEventTimestamp is a dict.
-  // Prefs DCHECKs if it's the wrong type, or not registered, so this is not
-  // actually needed.
-  //
-  // TODO(mmenke): Remove this histogram.
-  base::UmaHistogramBoolean("SafeBrowsing.MetricsCollector.IsPrefValid", true);
-
   for (auto state_map : mutable_state_dict) {
     for (auto event_map : state_map.second.GetDict()) {
       event_map.second.GetList().EraseIf([&](const auto& timestamp) {
@@ -356,25 +339,6 @@
     return;
   }
 
-  for (int event_type_int = 0; event_type_int <= EventType::kMaxValue;
-       event_type_int += 1) {
-    EventType event_type = static_cast<EventType>(event_type_int);
-    if (IsBypassEventType(event_type)) {
-      base::UmaHistogramCounts100(
-          "SafeBrowsing.EsbDisabled.BypassCountLast28Days." +
-              GetEventTypeMetricSuffix(event_type),
-          GetEventCountSince(UserState::kEnhancedProtection, event_type,
-                             base::Time::Now() - base::Days(28)));
-    }
-    if (IsSecuritySensitiveEventType(event_type)) {
-      base::UmaHistogramCounts100(
-          "SafeBrowsing.EsbDisabled.SecuritySensitiveCountLast28Days." +
-              GetEventTypeMetricSuffix(event_type),
-          GetEventCountSince(UserState::kEnhancedProtection, event_type,
-                             base::Time::Now() - base::Days(28)));
-    }
-  }
-
   absl::optional<SafeBrowsingMetricsCollector::Event> latest_bypass_event =
       GetLatestEventFromEventTypeFilter(
           UserState::kEnhancedProtection,
@@ -384,12 +348,6 @@
     base::UmaHistogramEnumeration(
         "SafeBrowsing.EsbDisabled.LastBypassEventType",
         latest_bypass_event->type);
-    base::UmaHistogramCustomTimes(
-        "SafeBrowsing.EsbDisabled.LastBypassEventInterval." +
-            GetEventTypeMetricSuffix(latest_bypass_event->type),
-        /* sample */ base::Time::Now() - latest_bypass_event->timestamp,
-        /* min */ base::Seconds(1),
-        /* max */ base::Days(1), /* buckets */ 50);
   }
 
   absl::optional<SafeBrowsingMetricsCollector::Event>
@@ -401,13 +359,6 @@
     base::UmaHistogramEnumeration(
         "SafeBrowsing.EsbDisabled.LastSecuritySensitiveEventType",
         latest_security_sensitive_event->type);
-    base::UmaHistogramCustomTimes(
-        "SafeBrowsing.EsbDisabled.LastSecuritySensitiveEventInterval." +
-            GetEventTypeMetricSuffix(latest_security_sensitive_event->type),
-        /* sample */ base::Time::Now() -
-            latest_security_sensitive_event->timestamp,
-        /* min */ base::Seconds(1),
-        /* max */ base::Days(1), /* buckets */ 50);
   }
 
   const absl::optional<Event> latest_enabled_event =
diff --git a/components/safe_browsing/core/browser/safe_browsing_metrics_collector_unittest.cc b/components/safe_browsing/core/browser/safe_browsing_metrics_collector_unittest.cc
index d115494..07e6875 100644
--- a/components/safe_browsing/core/browser/safe_browsing_metrics_collector_unittest.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_metrics_collector_unittest.cc
@@ -264,25 +264,6 @@
   histograms.ExpectUniqueSample("SafeBrowsing.EsbDisabled.LastBypassEventType",
                                 /* sample */ EventType::CSD_INTERSTITIAL_BYPASS,
                                 /* expected_count */ 1);
-  histograms.ExpectUniqueTimeSample(
-      "SafeBrowsing.EsbDisabled.LastBypassEventInterval.CsdInterstitialBypass",
-      /* sample */ base::Hours(1),
-      /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.EsbDisabled.BypassCountLast28Days."
-      "DatabaseInterstitialBypass",
-      /* sample */ 2,
-      /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.EsbDisabled.BypassCountLast28Days."
-      "CsdInterstitialBypass",
-      /* sample */ 2,
-      /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.EsbDisabled.BypassCountLast28Days."
-      "RealTimeInterstitialBypass",
-      /* sample */ 0,
-      /* expected_count */ 1);
 
   // Changing standard protection to enhanced protection shouldn't log the
   // metric.
@@ -298,30 +279,6 @@
   SetSafeBrowsingState(&pref_service_, SafeBrowsingState::NO_SAFE_BROWSING);
   histograms.ExpectTotalCount("SafeBrowsing.EsbDisabled.LastBypassEventType",
                               /* expected_count */ 2);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.EsbDisabled.LastBypassEventType",
-      /* sample */ EventType::REAL_TIME_INTERSTITIAL_BYPASS,
-      /* expected_count */ 1);
-  histograms.ExpectTimeBucketCount(
-      "SafeBrowsing.EsbDisabled.LastBypassEventInterval."
-      "RealTimeInterstitialBypass",
-      /* sample */ base::Days(1),
-      /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.EsbDisabled.BypassCountLast28Days."
-      "DatabaseInterstitialBypass",
-      /* sample */ 2,
-      /* expected_count */ 2);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.EsbDisabled.BypassCountLast28Days."
-      "CsdInterstitialBypass",
-      /* sample */ 2,
-      /* expected_count */ 2);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.EsbDisabled.BypassCountLast28Days."
-      "RealTimeInterstitialBypass",
-      /* sample */ 1,
-      /* expected_count */ 1);
 
   // Changing no protection to enhanced protection shouldn't log the metric.
   SetSafeBrowsingState(&pref_service_, SafeBrowsingState::ENHANCED_PROTECTION);
@@ -344,21 +301,6 @@
       "SafeBrowsing.EsbDisabled.LastSecuritySensitiveEventType",
       /* sample */ EventType::SECURITY_SENSITIVE_SAFE_BROWSING_INTERSTITIAL,
       /* expected_count */ 1);
-  histograms.ExpectUniqueTimeSample(
-      "SafeBrowsing.EsbDisabled.LastSecuritySensitiveEventInterval."
-      "SafeBrowsingInterstitial",
-      /* sample */ base::Hours(1),
-      /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.EsbDisabled.SecuritySensitiveCountLast28Days."
-      "SafeBrowsingInterstitial",
-      /* sample */ 1,
-      /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.EsbDisabled.SecuritySensitiveCountLast28Days."
-      "SSLInterstitial",
-      /* sample */ 0,
-      /* expected_count */ 1);
 
   // Changing standard protection to enhanced protection shouldn't log the
   // metric.
@@ -376,16 +318,6 @@
   histograms.ExpectTotalCount(
       "SafeBrowsing.EsbDisabled.LastSecuritySensitiveEventType",
       /* expected_count */ 2);
-  histograms.ExpectTimeBucketCount(
-      "SafeBrowsing.EsbDisabled.LastSecuritySensitiveEventInterval."
-      "SafeBrowsingInterstitial",
-      /* sample */ base::Days(1),
-      /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.EsbDisabled.SecuritySensitiveCountLast28Days."
-      "SafeBrowsingInterstitial",
-      /* sample */ 2,
-      /* expected_count */ 1);
 
   // Changing no protection to enhanced protection shouldn't log the metric.
   SetSafeBrowsingState(&pref_service_, SafeBrowsingState::ENHANCED_PROTECTION);
@@ -637,21 +569,6 @@
       "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection.AllEvents",
       /* sample */ 4,
       /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection."
-      "DatabaseInterstitialBypass",
-      /* sample */ 2,
-      /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection."
-      "CsdInterstitialBypass",
-      /* sample */ 1,
-      /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection."
-      "RealTimeInterstitialBypass",
-      /* sample */ 1,
-      /* expected_count */ 1);
   histograms.ExpectTotalCount(
       "SafeBrowsing.Daily.SecuritySensitiveCountLast28Days.EnhancedProtection."
       "AllEvents",
@@ -661,11 +578,6 @@
       "AllEvents",
       /* sample */ 2,
       /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.Daily.SecuritySensitiveCountLast28Days.EnhancedProtection."
-      "SafeBrowsingInterstitial",
-      /* sample */ 2,
-      /* expected_count */ 1);
 
   FastForwardAndAddEvent(base::Hours(1), EventType::CSD_INTERSTITIAL_BYPASS);
   task_environment_.FastForwardBy(base::Days(1));
@@ -676,11 +588,6 @@
       "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection.AllEvents",
       /* sample */ 5,
       /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection."
-      "CsdInterstitialBypass",
-      /* sample */ 2,
-      /* expected_count */ 1);
 
   task_environment_.FastForwardBy(base::Days(1));
   histograms.ExpectTotalCount(
@@ -690,11 +597,6 @@
       "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection.AllEvents",
       /* sample */ 5,
       /* expected_count */ 2);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection."
-      "CsdInterstitialBypass",
-      /* sample */ 2,
-      /* expected_count */ 2);
 }
 
 TEST_F(SafeBrowsingMetricsCollectorTest,
@@ -715,16 +617,6 @@
       "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection.AllEvents",
       /* sample */ 1,
       /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection."
-      "DatabaseInterstitialBypass",
-      /* sample */ 0,
-      /* expected_count */ 0);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection."
-      "DatabaseInterstitialBypass",
-      /* sample */ 1,
-      /* expected_count */ 1);
 
   task_environment_.FastForwardBy(base::Days(28));
   // The event is older than 28 days, so it shouldn't be counted.
@@ -732,11 +624,6 @@
       "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection.AllEvents",
       /* sample */ 0,
       /* expected_count */ 1);
-  histograms.ExpectBucketCount(
-      "SafeBrowsing.Daily.BypassCountLast28Days.EnhancedProtection."
-      "DatabaseInterstitialBypass",
-      /* sample */ 0,
-      /* expected_count */ 1);
 }
 
 TEST_F(SafeBrowsingMetricsCollectorTest,
@@ -790,10 +677,6 @@
   task_environment_.FastForwardBy(base::Days(1));
   // The CSD event is also removed because it was logged more than 30 days now.
   EXPECT_EQ(0u, csd_timestamps.size());
-
-  histograms.ExpectUniqueSample("SafeBrowsing.MetricsCollector.IsPrefValid",
-                                /* sample */ 1,
-                                /* expected_count */ 32);
 }
 
 TEST_F(SafeBrowsingMetricsCollectorTest, GetUserState) {
diff --git a/components/sessions/core/command_storage_backend.cc b/components/sessions/core/command_storage_backend.cc
index a4d6d75..981a148 100644
--- a/components/sessions/core/command_storage_backend.cc
+++ b/components/sessions/core/command_storage_backend.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_util.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/time/default_clock.h"
 #include "build/build_config.h"
 #include "components/sessions/core/session_constants.h"
 #include "components/sessions/core/session_service_commands.h"
@@ -423,6 +424,9 @@
 // CommandStorageBackend
 // -------------------------------------------------------------
 
+CommandStorageBackend::OpenFile::OpenFile() = default;
+CommandStorageBackend::OpenFile::~OpenFile() = default;
+
 // static
 const int CommandStorageBackend::kFileReadBufferSize = 1024;
 
@@ -434,12 +438,14 @@
     scoped_refptr<base::SequencedTaskRunner> owning_task_runner,
     const base::FilePath& path,
     SessionType type,
-    const std::vector<uint8_t>& decryption_key)
+    const std::vector<uint8_t>& decryption_key,
+    base::Clock* clock)
     : RefCountedDeleteOnSequence(owning_task_runner),
       type_(type),
       supplied_path_(path),
       initial_decryption_key_(decryption_key),
-      callback_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {
+      callback_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
+      clock_(clock ? clock : base::DefaultClock::GetInstance()) {
   // This is invoked on the main thread, don't do file access here.
 }
 
@@ -465,8 +471,9 @@
   // initial state has been supplied. To do otherwise would mean the file never
   // contains the marker, and would not be considered
   // valid. This includes first time through.
-  if (!truncate && (!file_ || !file_->IsValid()))
+  if (!truncate && !open_file_) {
     return;
+  }
 
   if (truncate) {
     CloseFile();
@@ -485,30 +492,38 @@
     DCHECK(crypto_key.empty());
   }
 
-  // Make sure and check |file_|, if opening the file failed |file_| will be
-  // null.
-  if (truncate || !file_ || !file_->IsValid())
+  // Make sure and check `open_file_`, if opening the file failed `open_file_`
+  // will be null.
+  if (truncate || !open_file_) {
     TruncateOrOpenFile();
+  }
 
-  // Check |file_| again as TruncateOrOpenFile() may fail.
-  if (file_ && file_->IsValid() &&
-      !AppendCommandsToFile(file_.get(), commands)) {
+  // Check `open_file_` again as TruncateOrOpenFile() may fail.
+  if (open_file_ && !AppendCommandsToFile(open_file_->file.get(), commands)) {
     CloseFile();
   }
 
-  if (truncate && file_ && file_->IsValid()) {
-    did_write_marker_ = true;
+  if (truncate && open_file_) {
+    // When `truncate` is true, a new file should be created, which means
+    // `did_write_marker` should be false.
+    DCHECK(!open_file_->did_write_marker);
+    open_file_->did_write_marker = true;
     if (last_file_with_valid_marker_) {
-      DCHECK_NE(*last_file_with_valid_marker_, current_path_);
+      // `last_file_with_valid_marker_` is only set after a truncation, which
+      // signals a new path should be used and that the two paths should not
+      // be equal (TruncateOrOpenFile() assigns a new path every time it's
+      // called).
+      DCHECK_NE(*last_file_with_valid_marker_, open_file_->path);
       base::DeleteFile(*last_file_with_valid_marker_);
       last_file_with_valid_marker_.reset();
     }
-    last_file_with_valid_marker_ = current_path_;
+    last_file_with_valid_marker_ = open_file_->path;
   }
 
-  // If `file_` is null, there was an error in writing.
-  if (!file_ && error_callback)
+  // If `open_file_` is null, there was an error in writing.
+  if (!open_file_ && error_callback) {
     callback_task_runner_->PostTask(FROM_HERE, std::move(error_callback));
+  }
 }
 
 // static
@@ -574,8 +589,6 @@
   }
   last_session_info_ = new_last_session_info;
 
-  // This ensures TruncateOrOpenFile() opens the file.
-  current_path_.clear();
   TruncateOrOpenFile();
 }
 
@@ -639,22 +652,27 @@
 }
 
 void CommandStorageBackend::CloseFile() {
-  file_.reset();
+  if (!open_file_) {
+    return;
+  }
+
+  // Close the file first, so that if we delete the file we won't have it open
+  // for writing (which may cause deletion to fail).
+  open_file_->file.reset();
 
   // If a marker wasn't written, no need to keep the current file.
-  if (!did_write_marker_ && !current_path_.empty())
-    base::DeleteFile(current_path_);
+  if (!open_file_->did_write_marker) {
+    base::DeleteFile(open_file_->path);
+  }
+
+  open_file_.reset();
 }
 
 void CommandStorageBackend::TruncateOrOpenFile() {
   DCHECK(inited_);
   CloseFile();
-  DCHECK(!file_);
-  base::Time new_timestamp = base::Time::Now();
-  // Ensure we don't reuse the current file (this is extremely unlikely to
-  // ever be true).
-  if (new_timestamp == timestamp_)
-    new_timestamp += base::Microseconds(1);
+  DCHECK(!open_file_);
+  base::Time new_timestamp = clock_->Now();
   if (last_session_info_) {
     // Ensure that the last session's timestamp is before the current file's.
     // This might not be true if the system clock has changed.
@@ -662,11 +680,20 @@
       new_timestamp = last_session_info_->timestamp + base::Microseconds(1);
     }
   }
+  // Ensure we don't reuse the timestamp, and that it's always increasing. If
+  // we didn't do this, and the clock time goes backwards, we could potentially
+  // reuse a filepath, resulting in writing on top of an existing file.
+  if (new_timestamp <= timestamp_) {
+    new_timestamp = timestamp_ + base::Microseconds(1);
+  }
   timestamp_ = new_timestamp;
-  current_path_ = FilePathFromTime(type_, supplied_path_, timestamp_);
-  file_ = OpenAndWriteHeader(current_path_);
+  std::unique_ptr<OpenFile> open_file = std::make_unique<OpenFile>();
+  open_file->path = FilePathFromTime(type_, supplied_path_, timestamp_);
+  open_file->file = OpenAndWriteHeader(open_file->path);
+  if (open_file->file) {
+    open_file_ = std::move(open_file);
+  }
   commands_written_ = 0;
-  did_write_marker_ = false;
 }
 
 std::unique_ptr<base::File> CommandStorageBackend::OpenAndWriteHeader(
@@ -769,8 +796,8 @@
 absl::optional<CommandStorageBackend::SessionInfo>
 CommandStorageBackend::FindLastSessionFile() const {
   // Determine the session with the most recent timestamp. This is called
-  // at startup, before `current_path_` is set, so no need to check it.
-  DCHECK(current_path_.empty());
+  // at startup, before a file has been opened for writing.
+  DCHECK(!open_file_);
   for (const SessionInfo& session : GetSessionFilesSortedByReverseTimestamp()) {
     if (CanUseFileForLastSession(session.path))
       return session;
@@ -788,8 +815,8 @@
 
 void CommandStorageBackend::DeleteLastSessionFiles() const {
   // Delete session files whose paths do not match the last session path. This
-  // at startup, before `current_path_` is set, so no need to check it.
-  DCHECK(current_path_.empty());
+  // is called at startup, before a file has been opened for writing.
+  DCHECK(!open_file_);
   for (const SessionInfo& session : GetSessionFilesSortedByReverseTimestamp()) {
     if (!last_session_info_ || session.path != last_session_info_->path)
       base::DeleteFile(session.path);
diff --git a/components/sessions/core/command_storage_backend.h b/components/sessions/core/command_storage_backend.h
index fbb6601..2b24d894 100644
--- a/components/sessions/core/command_storage_backend.h
+++ b/components/sessions/core/command_storage_backend.h
@@ -22,6 +22,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
+class Clock;
 class File;
 }
 
@@ -89,7 +90,8 @@
       scoped_refptr<base::SequencedTaskRunner> owning_task_runner,
       const base::FilePath& path,
       CommandStorageManager::SessionType type,
-      const std::vector<uint8_t>& decryption_key = {});
+      const std::vector<uint8_t>& decryption_key = {},
+      base::Clock* clock = nullptr);
   CommandStorageBackend(const CommandStorageBackend&) = delete;
   CommandStorageBackend& operator=(const CommandStorageBackend&) = delete;
 
@@ -97,7 +99,14 @@
   static bool IsValidFile(const base::FilePath& path);
 
   // Returns the path the files are being written to.
-  const base::FilePath& current_path() const { return current_path_; }
+  const base::FilePath current_path() const {
+    return open_file_ ? open_file_->path : base::FilePath();
+  }
+
+  bool IsFileOpen() const {
+    return open_file_.get() != nullptr;
+    ;
+  }
 
   base::SequencedTaskRunner* owning_task_runner() {
     return base::RefCountedDeleteOnSequence<
@@ -152,6 +161,16 @@
     base::Time timestamp;
   };
 
+  struct OpenFile {
+    OpenFile();
+    ~OpenFile();
+
+    base::FilePath path;
+    std::unique_ptr<base::File> file;
+    // Set to true once `kInitialStateMarkerCommandId` is written.
+    bool did_write_marker = false;
+  };
+
   ~CommandStorageBackend();
 
   // Performs initialization on the background task run, calling DoInit() if
@@ -243,11 +262,10 @@
   // TaskRunner that the callback is added to.
   scoped_refptr<base::SequencedTaskRunner> callback_task_runner_;
 
-  // Path commands are currently being saved to.
-  base::FilePath current_path_;
+  raw_ptr<base::Clock> clock_;
 
-  // This may be null, created as necessary.
-  std::unique_ptr<base::File> file_;
+  // File and path commands are being written.
+  std::unique_ptr<OpenFile> open_file_;
 
   // Whether DoInit() was called. DoInit() is called on the background task
   // runner.
@@ -259,9 +277,6 @@
   // Incremented every time a command is written.
   int commands_written_ = 0;
 
-  // Set to true once `kInitialStateMarkerCommandId` is written.
-  bool did_write_marker_ = false;
-
   // Timestamp when this session was started.
   base::Time timestamp_;
 
diff --git a/components/sessions/core/command_storage_backend_unittest.cc b/components/sessions/core/command_storage_backend_unittest.cc
index f9e3612..553cdbc1 100644
--- a/components/sessions/core/command_storage_backend_unittest.cc
+++ b/components/sessions/core/command_storage_backend_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/test/bind.h"
+#include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/sessions/core/command_storage_manager.h"
@@ -81,10 +82,11 @@
   }
 
   scoped_refptr<CommandStorageBackend> CreateBackend(
-      const std::vector<uint8_t>& decryption_key = {}) {
+      const std::vector<uint8_t>& decryption_key = {},
+      base::Clock* clock = nullptr) {
     return MakeRefCounted<CommandStorageBackend>(
         task_environment_.GetMainThreadTaskRunner(), file_path_,
-        CommandStorageManager::SessionType::kOther, decryption_key);
+        CommandStorageManager::SessionType::kOther, decryption_key, clock);
   }
 
   scoped_refptr<CommandStorageBackend> CreateBackendWithRestoreType() {
@@ -780,19 +782,17 @@
   SessionCommands commands;
   commands.push_back(CreateCommandFromData(data));
   backend->AppendCommands(std::move(commands), true, base::DoNothing());
-  const base::FilePath path1 = backend->current_path();
-  EXPECT_FALSE(path1.empty());
+  EXPECT_TRUE(backend->IsFileOpen());
 
   // Make appending fail, which should close the file.
   backend->ForceAppendCommandsToFailForTesting();
   backend->AppendCommands({}, false, base::DoNothing());
+  EXPECT_FALSE(backend->IsFileOpen());
 
-  // Append again, with another fail. Should attempt to reopen file.
+  // Append again, with another fail. Should attempt to reopen file and file.
   backend->ForceAppendCommandsToFailForTesting();
   backend->AppendCommands({}, true, base::DoNothing());
-  const base::FilePath path2 = backend->current_path();
-  EXPECT_FALSE(path2.empty());
-  EXPECT_NE(path1, path2);
+  EXPECT_FALSE(backend->IsFileOpen());
 
   // Reopen and read last session. Should get `data` and marker.
   backend = nullptr;
@@ -803,4 +803,35 @@
   AssertCommandEqualsData(data, commands[0].get());
 }
 
+TEST_F(CommandStorageBackendTest, PathTimeIncreases) {
+  base::SimpleTestClock test_clock;
+  test_clock.SetNow(base::Time::Now());
+  scoped_refptr<CommandStorageBackend> backend = CreateBackend({}, &test_clock);
+  // Write `data` and a marker.
+  struct TestData data = {11, "X"};
+  SessionCommands commands;
+  commands.push_back(CreateCommandFromData(data));
+  backend->AppendCommands(std::move(commands), true, base::DoNothing());
+  const base::FilePath path1 = backend->current_path();
+  EXPECT_FALSE(path1.empty());
+  base::Time path1_time;
+  EXPECT_TRUE(CommandStorageBackend::TimestampFromPath(path1, path1_time));
+
+  test_clock.Advance(base::Seconds(-1));
+  SessionCommands commands2;
+  commands2.push_back(CreateCommandFromData(data));
+  backend->AppendCommands(std::move(commands2), true, base::DoNothing());
+  const base::FilePath path2 = backend->current_path();
+  EXPECT_FALSE(path2.empty());
+  EXPECT_NE(path1, path2);
+  base::Time path2_time;
+  EXPECT_TRUE(CommandStorageBackend::TimestampFromPath(path2, path2_time));
+  // Even though the current time is before the previous time, the timestamp
+  // of the file should increase.
+  EXPECT_GT(path2_time, path1_time);
+  // Backend needs to be destroyed before test_clock so we don't end up with
+  // dangling reference.
+  backend.reset();
+}
+
 }  // namespace sessions
diff --git a/components/signin/core/browser/chrome_connected_header_helper.cc b/components/signin/core/browser/chrome_connected_header_helper.cc
index afa2234d..3eb6289 100644
--- a/components/signin/core/browser/chrome_connected_header_helper.cc
+++ b/components/signin/core/browser/chrome_connected_header_helper.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -15,7 +14,6 @@
 #include "build/chromeos_buildflags.h"
 #include "components/google/core/common/google_util.h"
 #include "components/signin/core/browser/cookie_settings_util.h"
-#include "components/signin/public/base/signin_switches.h"
 #include "components/signin/public/identity_manager/tribool.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
@@ -63,13 +61,6 @@
     return GAIA_SERVICE_TYPE_NONE;
 }
 
-bool NewRequestHeaderCheckOrder() {
-  // The result is computed once and cached because the code is on the hot path.
-  static bool new_order =
-      base::FeatureList::IsEnabled(switches::kNewSigninRequestHeaderCheckOrder);
-  return new_order;
-}
-
 }  // namespace
 
 const char kChromeConnectedCookieName[] = "CHROME_CONNECTED";
@@ -132,25 +123,13 @@
 bool ChromeConnectedHeaderHelper::ShouldBuildRequestHeader(
     const GURL& url,
     const content_settings::CookieSettings* cookie_settings) {
-  // The 'new order' refers to the order of the two checks performed in this
-  // function. In the new order the less expensive URL-based check is performed
-  // first in the most common case (non-Google URLs), and the cookie-based
-  // check is performed second.
-  bool new_order = NewRequestHeaderCheckOrder();
-
-  // Check if url is eligible for the header. New order.
-  if (new_order && !IsUrlEligibleForRequestHeader(url))
+  // Check if url is eligible for the header.
+  if (!IsUrlEligibleForRequestHeader(url)) {
     return false;
+  }
 
   // If signin cookies are not allowed, don't add the header.
-  if (!SettingsAllowSigninCookies(cookie_settings))
-    return false;
-
-  // Check if url is eligible for the header. Old order.
-  if (!new_order && !IsUrlEligibleForRequestHeader(url))
-    return false;
-
-  return true;
+  return SettingsAllowSigninCookies(cookie_settings);
 }
 
 bool ChromeConnectedHeaderHelper::IsUrlEligibleToIncludeGaiaId(
diff --git a/components/signin/public/base/signin_switches.cc b/components/signin/public/base/signin_switches.cc
index 40dcd9d..cc3ab23 100644
--- a/components/signin/public/base/signin_switches.cc
+++ b/components/signin/public/base/signin_switches.cc
@@ -17,13 +17,6 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
-// If enabled, performs the URL-based check first when proving that the
-// X-Chrome-Connected header is not needed in request headers on HTTP
-// redirects. The hypothesis is that this order of checks is faster to perform.
-BASE_FEATURE(kNewSigninRequestHeaderCheckOrder,
-             "NewSigninRequestHeaderCheckOrder",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Clears the token service before using it. This allows simulating the
 // expiration of credentials during testing.
 const char kClearTokenService[] = "clear-token-service";
diff --git a/components/signin/public/base/signin_switches.h b/components/signin/public/base/signin_switches.h
index 48419883..4edf49d 100644
--- a/components/signin/public/base/signin_switches.h
+++ b/components/signin/public/base/signin_switches.h
@@ -23,8 +23,6 @@
 BASE_DECLARE_FEATURE(kGaiaIdCacheInAccountManagerFacade);
 #endif
 
-BASE_DECLARE_FEATURE(kNewSigninRequestHeaderCheckOrder);
-
 extern const char kClearTokenService[];
 
 extern const char kDisableSigninScopedDeviceId[];
diff --git a/components/translate/content/android/translate_message.cc b/components/translate/content/android/translate_message.cc
index faece6a..aa3862a 100644
--- a/components/translate/content/android/translate_message.cc
+++ b/components/translate/content/android/translate_message.cc
@@ -15,6 +15,7 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/containers/contains.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
@@ -312,6 +313,11 @@
       ui_delegate_->Translate();
       break;
 
+    case State::kTranslating:
+      // Should not happen, but per https://crbug.com/1409304 it may, so add
+      // logging.
+      base::debug::DumpWithoutCrashing();
+      break;
     case State::kAfterTranslateWithAutoAlwaysConfirmation:
       // The user clicked "Undo" on a translated page when the
       // auto-always-translate confirmation message was showing, so turn off
@@ -336,7 +342,11 @@
       ui_delegate_->SetLanguageBlocked(false);
       bridge_->Dismiss(env);
       break;
-
+    case State::kDismissed:
+      // Should not happen, but per https://crbug.com/1409304 it may, so add
+      // logging.
+      base::debug::DumpWithoutCrashing();
+      break;
     default:
       NOTREACHED();
       break;
diff --git a/components/user_education/common/tutorial_description.h b/components/user_education/common/tutorial_description.h
index bec187a..774880a6 100644
--- a/components/user_education/common/tutorial_description.h
+++ b/components/user_education/common/tutorial_description.h
@@ -236,12 +236,12 @@
     }
 
     Step& InAnyContext() {
-      context_mode = TutorialDescription::ContextMode::kAny;
+      context_mode = ContextMode::kAny;
       return *this;
     }
 
     Step& InSameContext() {
-      context_mode = TutorialDescription::ContextMode::kFromPreviousStep;
+      context_mode = ContextMode::kFromPreviousStep;
       return *this;
     }
   };
@@ -369,6 +369,30 @@
                event_type_) {}
   };
 
+  // TutorialDescription::Create<"Prefix">(step1, step2, ...)
+  //
+  // Create a tutorial description with the given steps
+  // This will also generate the histograms with the given prefix
+  template <const char histogram_name[], typename... Args>
+  static TutorialDescription Create(Args&&... steps) {
+    TutorialDescription description;
+    description.steps = Steps(steps...);
+    description.histograms =
+        user_education::MakeTutorialHistograms<histogram_name>(
+            description.steps.size());
+    return description;
+  }
+
+  // TutorialDescription::Steps(step1, step2, {step3, step4}, ...)
+  //
+  // Turn steps and step vectors into a flattened vector of steps
+  template <typename... Args>
+  static std::vector<TutorialDescription::Step> Steps(Args&&... steps) {
+    std::vector<TutorialDescription::Step> flat_steps = {};
+    (AddStep(flat_steps, std::forward<Args>(steps)), ...);
+    return flat_steps;
+  }
+
   // the list of TutorialDescription steps
   std::vector<Step> steps;
 
@@ -381,6 +405,16 @@
   // cases this flag should be set to false so that the restart tutorial button
   // is not displayed.
   bool can_be_restarted = false;
+
+ private:
+  static void AddStep(std::vector<Step>& dest, Step step) {
+    dest.emplace_back(step);
+  }
+  static void AddStep(std::vector<Step>& dest, const std::vector<Step>& src) {
+    for (auto& step : src) {
+      dest.emplace_back(step);
+    }
+  }
 };
 
 }  // namespace user_education
diff --git a/components/user_education/common/tutorial_unittest.cc b/components/user_education/common/tutorial_unittest.cc
index e1b7b56..0091bd22 100644
--- a/components/user_education/common/tutorial_unittest.cc
+++ b/components/user_education/common/tutorial_unittest.cc
@@ -694,4 +694,53 @@
   EXPECT_CALL(aborted, Run).Times(1);
 }
 
+TEST_F(TutorialTest, RegisterTutorialWithCreate) {
+  std::unique_ptr<TutorialRegistry> registry =
+      std::make_unique<TutorialRegistry>();
+
+  {
+    auto description = TutorialDescription::Create<kHistogramName1>(
+        TutorialDescription::BubbleStep(kTestIdentifier1)
+            .SetBubbleBodyText(IDS_OK));
+    description.can_be_restarted = true;
+    EXPECT_TRUE(description.steps.size() == 1);
+
+    registry->AddTutorial(kTestTutorial1, std::move(description));
+  }
+
+  std::unique_ptr<HelpBubbleFactoryRegistry> bubble_factory_registry =
+      std::make_unique<HelpBubbleFactoryRegistry>();
+
+  EXPECT_TRUE(registry->IsTutorialRegistered(kTestTutorial1));
+}
+
+TEST_F(TutorialTest, RegisterTutorialWithCreateFromVector) {
+  std::unique_ptr<TutorialRegistry> registry =
+      std::make_unique<TutorialRegistry>();
+
+  {
+    TutorialDescription::Step first_step =
+        TutorialDescription::BubbleStep(kTestIdentifier1)
+            .SetBubbleBodyText(IDS_OK);
+
+    std::vector<TutorialDescription::Step> next_steps = {
+        TutorialDescription::BubbleStep(kTestIdentifier2)
+            .SetBubbleBodyText(IDS_OK),
+        TutorialDescription::BubbleStep(kTestIdentifier3)
+            .SetBubbleBodyText(IDS_OK)};
+
+    auto description =
+        TutorialDescription::Create<kHistogramName1>(first_step, next_steps);
+    description.can_be_restarted = true;
+    EXPECT_TRUE(description.steps.size() == 3);
+
+    registry->AddTutorial(kTestTutorial1, std::move(description));
+  }
+
+  std::unique_ptr<HelpBubbleFactoryRegistry> bubble_factory_registry =
+      std::make_unique<HelpBubbleFactoryRegistry>();
+
+  EXPECT_TRUE(registry->IsTutorialRegistered(kTestTutorial1));
+}
+
 }  // namespace user_education
diff --git a/components/variations/cros/BUILD.gn b/components/variations/cros/BUILD.gn
index d4440f4..2493c71 100644
--- a/components/variations/cros/BUILD.gn
+++ b/components/variations/cros/BUILD.gn
@@ -19,13 +19,13 @@
     "evaluate_seed.h",
   ]
   deps = [
+    ":proto",
     "//base",
     "//build:branding_buildflags",
     "//chromeos/crosapi/cpp",
     "//chromeos/crosapi/cpp:crosapi_constants",
     "//components/variations",
     "//components/variations/proto",
-    "//components/variations/proto:cros_safe_seed_proto",
     "//components/variations/service",
   ]
 
@@ -38,12 +38,12 @@
   sources = [ "evaluate_seed_unittest.cc" ]
   deps = [
     ":evaluate_seed_lib",
+    ":proto",
     "//base",
     "//base/test:test_support",
     "//build:branding_buildflags",
     "//components/test:test_support",
     "//components/variations",
-    "//components/variations/proto:cros_safe_seed_proto",
     "//testing/gmock",
     "//testing/gtest",
   ]
@@ -56,3 +56,9 @@
     "//components/test:test_support",
   ]
 }
+
+proto_library("proto") {
+  sources = [ "//third_party/cros_system_api/dbus/featured/featured.proto" ]
+
+  proto_out_dir = "components/variations/cros"
+}
diff --git a/components/variations/cros/evaluate_seed.cc b/components/variations/cros/evaluate_seed.cc
index fa038ac..2065e16 100644
--- a/components/variations/cros/evaluate_seed.cc
+++ b/components/variations/cros/evaluate_seed.cc
@@ -12,7 +12,7 @@
 #include "build/branding_buildflags.h"
 #include "chromeos/crosapi/cpp/channel_to_enum.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
-#include "components/variations/proto/cros_safe_seed.pb.h"
+#include "components/variations/cros/featured.pb.h"
 #include "components/variations/proto/study.pb.h"
 #include "components/variations/service/variations_field_trial_creator.h"
 
@@ -60,7 +60,7 @@
 
 absl::optional<SafeSeed> GetSafeSeedData(const base::CommandLine* command_line,
                                          FILE* stream) {
-  variations::SeedDetails safe_seed;
+  featured::SeedDetails safe_seed;
   if (command_line->HasSwitch(kSafeSeedSwitch)) {
     // Read safe seed from |stream|.
     std::string safe_seed_data;
diff --git a/components/variations/cros/evaluate_seed.h b/components/variations/cros/evaluate_seed.h
index 66194a6..7ed290a 100644
--- a/components/variations/cros/evaluate_seed.h
+++ b/components/variations/cros/evaluate_seed.h
@@ -12,7 +12,7 @@
 
 #include "base/command_line.h"
 #include "components/variations/client_filterable_state.h"
-#include "components/variations/proto/cros_safe_seed.pb.h"
+#include "components/variations/cros/featured.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace variations::evaluate_seed {
@@ -23,7 +23,7 @@
 
 struct SafeSeed {
   bool use_safe_seed = false;
-  variations::SeedDetails seed_data;
+  featured::SeedDetails seed_data;
 };
 
 // Read the safe seed data from |stream|, if and only if the |command_line|
diff --git a/components/variations/cros/evaluate_seed_unittest.cc b/components/variations/cros/evaluate_seed_unittest.cc
index 52b9a36..c64d5376 100644
--- a/components/variations/cros/evaluate_seed_unittest.cc
+++ b/components/variations/cros/evaluate_seed_unittest.cc
@@ -8,7 +8,7 @@
 #include "base/test/scoped_chromeos_version_info.h"
 #include "build/branding_buildflags.h"
 #include "components/variations/client_filterable_state.h"
-#include "components/variations/proto/cros_safe_seed.pb.h"
+#include "components/variations/cros/featured.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -99,7 +99,7 @@
 
 // Should ignore data if flag is off.
 TEST(VariationsCrosEvaluateSeed, GetSafeSeedData_Off) {
-  variations::SeedDetails safe_seed;
+  featured::SeedDetails safe_seed;
   safe_seed.set_compressed_data("some text");
   std::string text;
   safe_seed.SerializeToString(&text);
@@ -108,7 +108,7 @@
 
   base::CommandLine command_line({"evaluate_seed"});
   auto data = GetSafeSeedData(&command_line, stream);
-  variations::SeedDetails empty_seed;
+  featured::SeedDetails empty_seed;
   ASSERT_TRUE(data.has_value());
   EXPECT_FALSE(data.value().use_safe_seed);
   EXPECT_THAT(data.value().seed_data, EqualsProto(empty_seed));
@@ -116,7 +116,7 @@
 
 // Should return specified data via stream if flag is on.
 TEST(VariationsCrosEvaluateSeed, GetSafeSeedData_On) {
-  variations::SeedDetails safe_seed;
+  featured::SeedDetails safe_seed;
   safe_seed.set_compressed_data("some text");
   std::string text;
   safe_seed.SerializeToString(&text);
@@ -132,7 +132,7 @@
 
 // Should not attempt to read stream if flag is not on.
 TEST(VariationsCrosEvaluateSeed, GetSafeSeedData_Off_FailRead) {
-  variations::SeedDetails safe_seed;
+  featured::SeedDetails safe_seed;
   safe_seed.set_compressed_data("some text");
   std::string text;
   safe_seed.SerializeToString(&text);
@@ -141,7 +141,7 @@
 
   base::CommandLine command_line({"evaluate_seed"});
   auto data = GetSafeSeedData(&command_line, stream);
-  variations::SeedDetails empty_seed;
+  featured::SeedDetails empty_seed;
   ASSERT_TRUE(data.has_value());
   EXPECT_FALSE(data.value().use_safe_seed);
   EXPECT_THAT(data.value().seed_data, EqualsProto(empty_seed));
@@ -149,7 +149,7 @@
 
 // If flag is on and reading fails, should return nullopt.
 TEST(VariationsCrosEvaluateSeed, GetSafeSeedData_On_FailRead) {
-  variations::SeedDetails safe_seed;
+  featured::SeedDetails safe_seed;
   safe_seed.set_compressed_data("some text");
   std::string text;
   safe_seed.SerializeToString(&text);
diff --git a/components/variations/proto/BUILD.gn b/components/variations/proto/BUILD.gn
index 2cad9d17..8bedf01 100644
--- a/components/variations/proto/BUILD.gn
+++ b/components/variations/proto/BUILD.gn
@@ -45,11 +45,3 @@
     deps = [ "devtools" ]
   }
 }
-
-# This proto is only used on CrOS. Other platforms do not support this proto
-# and should not use this library.
-proto_library("cros_safe_seed_proto") {
-  sources = [ "cros_safe_seed.proto" ]
-
-  proto_out_dir = "components/variations/proto"
-}
diff --git a/components/variations/proto/OWNERS b/components/variations/proto/OWNERS
deleted file mode 100644
index 27ba794..0000000
--- a/components/variations/proto/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-per-file cros_safe_seed.proto=file://components/variations/cros/OWNERS
diff --git a/components/variations/proto/cros_safe_seed.proto b/components/variations/proto/cros_safe_seed.proto
deleted file mode 100644
index cd8ce156..0000000
--- a/components/variations/proto/cros_safe_seed.proto
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2023 The ChromiumOS Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-syntax = "proto3";
-
-option optimize_for = LITE_RUNTIME;
-
-// This file defines messages necessary for CrOS early boot experimentation
-package variations;
-
-// Information about the last known "safe" seed.
-//
-// Any changes to this message must also be reflected in
-// third_party/cros_system_api/dbus/featured/featured.proto. These messages must
-// be kept in sync.
-message SeedDetails {
-  // Last known "safe" seed represented as the base64-encoded gzip-compressed
-  // serialized form of the variations seed protobuf.
-  string compressed_data = 1;
-  // The active client locale that was successfully used in association with the
-  // last known "safe" seed.
-  string locale = 4;
-  // Milestone with which the last known "safe" seed was fetched.
-  int32 milestone = 5;
-  // String form of pair <Chrome version string, country code string> (eg.
-  // "106.0.5249.119,us") representing the country used for filtering permanent
-  // consistency studies until the next time Chrome is updated.
-  string permanent_consistency_country = 6;
-  // A country code string representing the country used for evaluating session
-  // consistency studies.
-  string session_consistency_country = 7;
-  // Digital signature of the last known "safe" seed's binary data. Empty if
-  // there is no known "safe" seed.
-  string signature = 8;
-  // The serialized base::Time used for safe seed expiry checks. This is usually
-  // the time at which the last known "safe" seed was received; however, it
-  // could be a build timestamp if the received date is unknown. An empty
-  // (default-constructed) base::Time if there is no known "safe" seed. This is
-  // a server-provided timestamp in milliseconds.
-  int64 date = 9;
-  // The serialized base::Time from the fetch corresponding to the last known
-  // "safe" seed. This is a client timestamp in milliseconds.
-  int64 fetch_time = 10;
-
-  // Tags 2 and 3 are reserved since they have been deleted.
-  // These tags were used for older versions of the date and fetch_time fields,
-  // which were both of type google.protobuf.Timestamp. Importing
-  // google.protobuf.Timestamp is not supported in Chromium's
-  // third_party/cros_system_api, so both fields were changed to type int64 (the
-  // same data type finch protos use for dates).
-  reserved 2, 3;
-}
diff --git a/components/viz/service/display/copy_output_scaling_pixeltest.cc b/components/viz/service/display/copy_output_scaling_pixeltest.cc
index 06f842a..b709581 100644
--- a/components/viz/service/display/copy_output_scaling_pixeltest.cc
+++ b/components/viz/service/display/copy_output_scaling_pixeltest.cc
@@ -216,7 +216,7 @@
       gfx::Rect rect = smaller_pass_rects[i] - copy_rect.OffsetFromOrigin();
       rect = copy_output::ComputeResultRect(rect, scale_from_, scale_to_);
       expected_bitmap.erase(
-          smaller_pass_colors[i], nullptr /* SkColorSpace* colorSpace */,
+          smaller_pass_colors[i],
           SkIRect{rect.x(), rect.y(), rect.right(), rect.bottom()});
     }
 
diff --git a/components/viz/service/display/resolved_frame_data.cc b/components/viz/service/display/resolved_frame_data.cc
index 641c102..3605f04 100644
--- a/components/viz/service/display/resolved_frame_data.cc
+++ b/components/viz/service/display/resolved_frame_data.cc
@@ -166,6 +166,9 @@
         }
 
         referenced_resources.push_back(resource_id);
+
+        // Update `ResolvedQuadData::remapped_resources` to have the remapped
+        // display resource_id.
         resource_id = iter->second;
       }
     }
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
index 341f90e7b..add6549 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
@@ -132,7 +132,7 @@
   SkBitmap expected;
   expected.allocPixels(SkImageInfo::MakeN32Premul(
       output_rect.width(), output_rect.height(), color_space.ToSkColorSpace()));
-  expected.eraseColor(kOutputColor, /*colorSpace=*/nullptr);
+  expected.eraseColor(kOutputColor);
 
   EXPECT_TRUE(
       cc::MatchesBitmap(result_bitmap, expected, cc::ExactPixelComparator()));
diff --git a/components/webapps/browser/installable/installable_data.h b/components/webapps/browser/installable/installable_data.h
index 39bb991..e375f34 100644
--- a/components/webapps/browser/installable/installable_data.h
+++ b/components/webapps/browser/installable/installable_data.h
@@ -13,6 +13,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/raw_ref.h"
 #include "components/webapps/browser/installable/installable_logging.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "url/gurl.h"
diff --git a/components/webapps/browser/installable/installable_logging.cc b/components/webapps/browser/installable/installable_logging.cc
index f976241..fbd38640 100644
--- a/components/webapps/browser/installable/installable_logging.cc
+++ b/components/webapps/browser/installable/installable_logging.cc
@@ -11,6 +11,7 @@
 #include "components/webapps/browser/installable/installable_manager.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 
 namespace webapps {
 
diff --git a/components/webapps/browser/installable/installable_logging.h b/components/webapps/browser/installable/installable_logging.h
index e1e7aaa..fb380dbf 100644
--- a/components/webapps/browser/installable/installable_logging.h
+++ b/components/webapps/browser/installable/installable_logging.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-forward.h"
 
 namespace content {
 struct InstallabilityError;
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.h b/content/browser/attribution_reporting/attribution_storage_sql.h
index 265c179..d1d6df1 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.h
+++ b/content/browser/attribution_reporting/attribution_storage_sql.h
@@ -64,7 +64,7 @@
   //
   // Note that all versions >=15 were introduced during the transitional state
   // of the Attribution Reporting API and can be removed when done.
-  static constexpr int kDeprecatedVersionNumber = 34;
+  static constexpr int kDeprecatedVersionNumber = 35;
 
   static_assert(kCompatibleVersionNumber <= kCurrentVersionNumber);
   static_assert(kDeprecatedVersionNumber < kCompatibleVersionNumber);
diff --git a/content/browser/attribution_reporting/attribution_storage_sql_migrations.cc b/content/browser/attribution_reporting/attribution_storage_sql_migrations.cc
index 6b05a944..7d2da08 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql_migrations.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql_migrations.cc
@@ -55,23 +55,6 @@
          transaction.Commit();
 }
 
-bool To36(sql::Database& db) {
-  static constexpr char kDropOldIndexSql[] = "DROP INDEX sources_by_origin";
-  if (!db.Execute(kDropOldIndexSql)) {
-    return false;
-  }
-
-  static constexpr char kCreateNewIndexSql[] =
-      "CREATE INDEX active_sources_by_source_origin "
-      "ON sources(source_origin)"
-      "WHERE event_level_active=1 OR aggregatable_active=1";
-  if (!db.Execute(kCreateNewIndexSql)) {
-    return false;
-  }
-
-  return true;
-}
-
 bool To37(sql::Database& db) {
   static constexpr char kNewDedupKeyTableSql[] =
       "CREATE TABLE new_dedup_keys("
@@ -685,11 +668,10 @@
     start_timestamp = base::ThreadTicks::Now();
   }
 
-  static_assert(AttributionStorageSql::kDeprecatedVersionNumber + 1 == 35,
+  static_assert(AttributionStorageSql::kDeprecatedVersionNumber + 1 == 36,
                 "Remove migration(s) below.");
 
-  bool ok = MaybeMigrate(db, meta_table, 35, &To36) &&
-            MaybeMigrate(db, meta_table, 36, &To37) &&
+  bool ok = MaybeMigrate(db, meta_table, 36, &To37) &&
             MaybeMigrate(db, meta_table, 37, &To38) &&
             MaybeMigrate(db, meta_table, 38, &To39) &&
             MaybeMigrate(db, meta_table, 39, &To40) &&
diff --git a/content/browser/attribution_reporting/attribution_storage_sql_migrations_unittest.cc b/content/browser/attribution_reporting/attribution_storage_sql_migrations_unittest.cc
index e4fe8bd..11c76be 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql_migrations_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql_migrations_unittest.cc
@@ -202,40 +202,6 @@
   histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 0);
 }
 
-TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion35ToCurrent) {
-  base::HistogramTester histograms;
-  LoadDatabase(GetVersionFilePath(35), DbPath());
-
-  // Verify pre-conditions.
-  {
-    sql::Database db;
-    ASSERT_TRUE(db.Open(DbPath()));
-    ASSERT_TRUE(db.DoesIndexExist("sources_by_origin"));
-    ASSERT_FALSE(db.DoesIndexExist("active_sources_by_source_origin"));
-  }
-
-  MigrateDatabase();
-
-  // Verify schema is current.
-  {
-    sql::Database db;
-    ASSERT_TRUE(db.Open(DbPath()));
-
-    CheckVersionNumbers(&db);
-
-    // Compare normalized schemas
-    EXPECT_EQ(NormalizeSchema(GetCurrentSchema()),
-              NormalizeSchema(db.GetSchema()));
-
-    ASSERT_FALSE(db.DoesIndexExist("sources_by_origin"));
-    ASSERT_TRUE(db.DoesIndexExist("active_sources_by_source_origin"));
-  }
-
-  // DB creation histograms should be recorded.
-  histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
-  histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
-}
-
 TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion36ToCurrent) {
   base::HistogramTester histograms;
   LoadDatabase(GetVersionFilePath(36), DbPath());
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 6dd8568..07bfa4b 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -818,9 +818,10 @@
   // while avoiding doing so in unit tests by making it explicitly enabled here.
   GpuDataManagerImpl::GetInstance()->StartUmaTimer();
 
-#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) || BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) || BUILDFLAG(IS_ANDROID) || \
+    BUILDFLAG(IS_IOS)
   // Single-process is an unsupported and not fully tested mode, so
-  // don't enable it for official Chrome builds (except on Android).
+  // don't enable it for official Chrome builds (except on Android and iOS).
   if (parsed_command_line_->HasSwitch(switches::kSingleProcess))
     RenderProcessHost::SetRunRendererInProcess(true);
 #endif
diff --git a/content/browser/browsing_topics/header_util.cc b/content/browser/browsing_topics/header_util.cc
index 1ee7793..0b2f24b2 100644
--- a/content/browser/browsing_topics/header_util.cc
+++ b/content/browser/browsing_topics/header_util.cc
@@ -40,7 +40,8 @@
     const url::Origin& caller_origin,
     RenderFrameHost& request_initiator_frame,
     browsing_topics::ApiCallerSource caller_source) {
-  DCHECK_EQ(caller_source, browsing_topics::ApiCallerSource::kFetch);
+  DCHECK(caller_source == browsing_topics::ApiCallerSource::kFetch ||
+         caller_source == browsing_topics::ApiCallerSource::kIframeAttribute);
 
   std::string header_value;
   headers.GetNormalizedHeader("Observe-Browsing-Topics", &header_value);
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 09e2e34..a508bd0c 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -43,6 +43,7 @@
 #include "components/attribution_reporting/os_support.mojom.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
+#include "content/browser/browsing_topics/header_util.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/client_hints/client_hints.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
@@ -1052,6 +1053,81 @@
                                                   tokens, base::Time::Now());
 }
 
+// Returns the topics header for a navigation request. Returns absl::nullopt if
+// the request isn't eligible for topics. This should align with the handling in
+// `GetTopicsHeaderValueForSubresourceRequest()`.
+absl::optional<std::string> GetTopicsHeaderValueForNavigationRequest(
+    FrameTreeNode* frame_tree_node,
+    const GURL& url) {
+  // Skip if the <iframe> does not have the "browsingtopics" opt-in attribute.
+  if (!frame_tree_node->browsing_topics()) {
+    return absl::nullopt;
+  }
+
+  RenderFrameHostImpl* rfh = frame_tree_node->current_frame_host();
+
+  // Skip top frame navigation.
+  // TODO(crbug.com/1424261): This should be checked at the mojom boundary of
+  // RenderFrameHostImpl::DidChangeIframeAttributes, and should be a DCHECK
+  // here.
+  if (rfh->is_main_frame()) {
+    return absl::nullopt;
+  }
+
+  // Skip fenced frames.
+  if (rfh->IsNestedWithinFencedFrame()) {
+    return absl::nullopt;
+  }
+
+  // Skip inactive pages (e.g. portal, prerendered pages).
+  if (!rfh->GetPage().IsPrimary()) {
+    return absl::nullopt;
+  }
+
+  // TODO(crbug.com/1244137): IsPrimary() doesn't actually detect portals yet.
+  // Remove this when it does.
+  if (!static_cast<RenderFrameHostImpl*>(rfh->GetMainFrame())
+           ->IsOutermostMainFrame()) {
+    return absl::nullopt;
+  }
+
+  url::Origin origin = url::Origin::Create(url);
+  if (origin.opaque()) {
+    return absl::nullopt;
+  }
+
+  if (!network::IsOriginPotentiallyTrustworthy(origin)) {
+    return absl::nullopt;
+  }
+
+  const blink::PermissionsPolicy* parent_policy =
+      rfh->GetParent()->permissions_policy();
+
+  DCHECK(parent_policy);
+
+  if (!parent_policy->IsFeatureEnabledForOrigin(
+          blink::mojom::PermissionsPolicyFeature::kBrowsingTopics, origin) ||
+      !parent_policy->IsFeatureEnabledForOrigin(
+          blink::mojom::PermissionsPolicyFeature::
+              kBrowsingTopicsBackwardCompatible,
+          origin)) {
+    return absl::nullopt;
+  }
+
+  std::vector<blink::mojom::EpochTopicPtr> topics;
+  bool topics_eligible = GetContentClient()->browser()->HandleTopicsWebApi(
+      origin, rfh->GetMainFrame(),
+      browsing_topics::ApiCallerSource::kIframeAttribute,
+      /*get_topics=*/true,
+      /*observe=*/false, topics);
+
+  if (!topics_eligible) {
+    return absl::nullopt;
+  }
+
+  return DeriveTopicsHeaderValue(topics);
+}
+
 }  // namespace
 
 NavigationRequest::PrerenderActivationNavigationState::
@@ -1865,6 +1941,16 @@
       headers.GetHeader(net::HttpRequestHeaders::kContentType,
                         &commit_params_->post_content_type);
     }
+
+    absl::optional<std::string> topics_header_value =
+        GetTopicsHeaderValueForNavigationRequest(frame_tree_node,
+                                                 common_params_->url);
+
+    topics_eligible_ = topics_header_value.has_value();
+
+    if (topics_eligible_) {
+      headers.SetHeader(kBrowsingTopicsRequestHeaderKey, *topics_header_value);
+    }
   }
 
   begin_params_->headers = headers.ToString();
@@ -4923,6 +5009,44 @@
 
   net::HttpRequestHeaders modified_headers = TakeModifiedRequestHeaders();
   std::vector<std::string> removed_headers = TakeRemovedRequestHeaders();
+
+  // The topics a request is allowed to see can change within its redirect
+  // chain thus we need to recalculate them. For example, different caller
+  // origins (i.e. navigation URL's origin) may receive different topics, as the
+  // callers can only get the topics about the sites they were on. Besides,
+  // regardless of cross-origin-ness, the timestamp can also affect the
+  // candidate epochs where the topics are derived from, thus resulting in
+  // different topics across redirects.
+  if (topics_eligible_) {
+    topics_eligible_ = false;
+
+    // Removes the topics header from the request that was passed on from the
+    // previous one.
+    removed_headers.push_back(kBrowsingTopicsRequestHeaderKey);
+
+    // At this point we may not have a valid `GetRenderFrameHost()` if the
+    // navigation is during a cross-site redirect. Thus, pass in the current/old
+    // RenderFrameHost here. This is fine, because it should still give us the
+    // desired IsPrimary() status and the desired top-level frame that
+    // `HandleTopicsEligibleResponse()` is interested in knowing.
+    HandleTopicsEligibleResponse(
+        *commit_params_->redirect_response.back().get()->headers.get(),
+        url::Origin::Create(commit_params_->redirects.back()),
+        *frame_tree_node_->current_frame_host(),
+        browsing_topics::ApiCallerSource::kIframeAttribute);
+  }
+
+  absl::optional<std::string> topics_header_value =
+      GetTopicsHeaderValueForNavigationRequest(frame_tree_node_,
+                                               common_params_->url);
+
+  topics_eligible_ = topics_header_value.has_value();
+
+  if (topics_eligible_) {
+    modified_headers.SetHeader(kBrowsingTopicsRequestHeaderKey,
+                               *topics_header_value);
+  }
+
   // Removes all Client Hints from the request, that were passed on from the
   // previous one.
   for (const auto& elem : network::GetClientHintToNameMap()) {
@@ -5295,6 +5419,10 @@
     }
   }
 
+  if (topics_eligible_) {
+    topics_eligible_ = false;
+  }
+
   ReadyToCommitNavigation(true /* is_error */);
 
   PopulateDocumentTokenForCrossDocumentNavigation();
@@ -5387,6 +5515,17 @@
       frame_tree_node()->frame_tree().GetSessionStorageKey(
           commit_params_->storage_key);
 
+  if (topics_eligible_) {
+    topics_eligible_ = false;
+
+    if (response()) {
+      HandleTopicsEligibleResponse(
+          *response()->headers.get(), url::Origin::Create(common_params_->url),
+          *GetRenderFrameHost(),
+          browsing_topics::ApiCallerSource::kIframeAttribute);
+    }
+  }
+
   if (IsServedFromBackForwardCache() || IsPrerenderedPageActivation()) {
     CommitPageActivation();
     return;
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index a852b36..5d292fd 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -2417,6 +2417,13 @@
   // reset.
   bool force_new_browsing_instance_ = false;
 
+  // Whether the ongoing navigation resource request is eligible for topics
+  // calculation. This is set before the initial request and each subsequent
+  // redirect. If `topics_eligible_` is true, the request headers will contain
+  // the "Sec-Browsing-Topics" header, and if the corresponding response headers
+  // contain "Observe-Browsing-Topics: ?1", a topic observation will be stored.
+  bool topics_eligible_ = false;
+
   // A WeakPtr for the BindContext associated with topics loader factory for the
   // committing document. This will be set in `CommitNavigation()`, and can
   // become null if the corresponding factory is destroyed. Upon
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index d419704f..595efc8 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -460,6 +460,17 @@
   return visualizer_.DepictFrameTree(node);
 }
 
+std::string SitePerProcessBrowserTestBase::WaitForMessageScript(
+    const std::string& result_expression) {
+  return base::StringPrintf(
+      "var onMessagePromise = new Promise(resolve => {"
+      "  window.addEventListener('message', function(event) {"
+      "    resolve(%s);"
+      "  });"
+      "});",
+      result_expression.c_str());
+}
+
 void SitePerProcessBrowserTestBase::SetUpCommandLine(
     base::CommandLine* command_line) {
   ContentBrowserTest::SetUpCommandLine(command_line);
@@ -8370,26 +8381,29 @@
   // Make sure the subframe can communicate to both the root remote frame
   // (where the postMessage should go to the current RenderFrameHost rather
   // than the pending one) and its sibling remote frame in the a.com process.
-  EXPECT_TRUE(ExecJs(child->current_frame_host(),
-                     "window.addEventListener('message', function(event) {\n"
-                     "  domAutomationController.send(event.data);\n"
-                     "});"));
+  EXPECT_TRUE(
+      ExecJs(child->current_frame_host(), WaitForMessageScript("event.data")));
+  EXPECT_TRUE(ExecJs(child, "parent.postMessage('root-ping', '*')"));
   EXPECT_EQ("root-ping-reply",
-            EvalJs(child, "parent.postMessage('root-ping', '*')",
-                   EXECUTE_SCRIPT_USE_MANUAL_REPLY));
+            EvalJs(child->current_frame_host(), "onMessagePromise"));
 
+  EXPECT_TRUE(
+      ExecJs(child->current_frame_host(), WaitForMessageScript("event.data")));
+  EXPECT_TRUE(
+      ExecJs(child, "parent.frames[1].postMessage('sibling-ping', '*')"));
   EXPECT_EQ("sibling-ping-subframe-reply",
-            EvalJs(child, "parent.frames[1].postMessage('sibling-ping', '*')",
-                   EXECUTE_SCRIPT_USE_MANUAL_REPLY));
+            EvalJs(child->current_frame_host(), "onMessagePromise"));
 
   // Cancel the pending main frame navigation, and verify that the subframe can
   // still communicate with the (old) main frame.
   root->navigator().CancelNavigation(root, NavigationDiscardReason::kCancelled);
   EXPECT_FALSE(root->render_manager()->speculative_frame_host());
 
+  EXPECT_TRUE(
+      ExecJs(child->current_frame_host(), WaitForMessageScript("event.data")));
+  EXPECT_TRUE(ExecJs(child, "parent.postMessage('root-ping', '*')"));
   EXPECT_EQ("root-ping-reply",
-            EvalJs(child, "parent.postMessage('root-ping', '*')",
-                   EXECUTE_SCRIPT_USE_MANUAL_REPLY));
+            EvalJs(child->current_frame_host(), "onMessagePromise"));
 }
 
 // Similar to TwoCrossSitePendingNavigations* tests above, but checks the case
@@ -8438,25 +8452,25 @@
   // Make sure the second tab can communicate to its (old) opener remote frame.
   // The postMessage should go to the current RenderFrameHost rather than the
   // pending one in the first tab's main frame.
-  EXPECT_TRUE(ExecJs(popup_shell->web_contents(),
-                     "window.addEventListener('message', function(event) {\n"
-                     "  domAutomationController.send(event.data);\n"
-                     "});"));
+  EXPECT_TRUE(
+      ExecJs(popup_shell->web_contents(), WaitForMessageScript("event.data")));
 
+  EXPECT_TRUE(ExecJs(popup_shell->web_contents(),
+                     "opener.postMessage('opener-ping', '*');"));
   EXPECT_EQ("opener-ping-reply",
-            EvalJs(popup_shell->web_contents(),
-                   "opener.postMessage('opener-ping', '*');",
-                   EXECUTE_SCRIPT_USE_MANUAL_REPLY));
+            EvalJs(popup_shell->web_contents(), "onMessagePromise"));
 
   // Cancel the pending main frame navigation, and verify that the subframe can
   // still communicate with the (old) main frame.
   root->navigator().CancelNavigation(root, NavigationDiscardReason::kCancelled);
   EXPECT_FALSE(root->render_manager()->speculative_frame_host());
 
+  EXPECT_TRUE(
+      ExecJs(popup_shell->web_contents(), WaitForMessageScript("event.data")));
+  EXPECT_TRUE(ExecJs(popup_shell->web_contents(),
+                     "opener.postMessage('opener-ping', '*')"));
   EXPECT_EQ("opener-ping-reply",
-            EvalJs(popup_shell->web_contents(),
-                   "opener.postMessage('opener-ping', '*')",
-                   EXECUTE_SCRIPT_USE_MANUAL_REPLY));
+            EvalJs(popup_shell->web_contents(), "onMessagePromise"));
 }
 
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
@@ -10714,20 +10728,18 @@
 
   // Add an onmessage handler to the subframe to send back a bool of whether
   // the subframe has focus.
-  EXPECT_TRUE(ExecJs(root->child_at(0),
-                     "window.addEventListener('message', function(event) {\n"
-                     "  domAutomationController.send(document.hasFocus());\n"
-                     "});"));
+  EXPECT_TRUE(
+      ExecJs(root->child_at(0), WaitForMessageScript("document.hasFocus()")));
 
   // Now, send a postMessage from main frame to subframe, and then focus the
   // subframe in the same script.  postMessage should be scheduled after the
   // focus() call, so the IPC to focus the subframe should arrive before the
   // postMessage IPC, and the subframe should already know that it's focused in
   // the onmessage handler.
-  EXPECT_EQ(true, EvalJs(root,
+  EXPECT_EQ(true, ExecJs(root,
                          "frames[0].postMessage('','*');\n"
-                         "frames[0].focus();\n",
-                         EXECUTE_SCRIPT_USE_MANUAL_REPLY));
+                         "frames[0].focus();\n"));
+  EXPECT_EQ(true, EvalJs(root->child_at(0), "onMessagePromise"));
 }
 
 // Ensure that if a cross-process postMessage is scheduled, and then the target
diff --git a/content/browser/site_per_process_browsertest.h b/content/browser/site_per_process_browsertest.h
index 29c9eef..15a1392 100644
--- a/content/browser/site_per_process_browsertest.h
+++ b/content/browser/site_per_process_browsertest.h
@@ -30,6 +30,8 @@
  protected:
   std::string DepictFrameTree(FrameTreeNode* node);
 
+  std::string WaitForMessageScript(const std::string& result_expression);
+
   void SetUpCommandLine(base::CommandLine* command_line) override;
   void SetUpOnMainThread() override;
 
diff --git a/content/browser/site_per_process_layout_browsertest.cc b/content/browser/site_per_process_layout_browsertest.cc
index 923d031..a2b96ce 100644
--- a/content/browser/site_per_process_layout_browsertest.cc
+++ b/content/browser/site_per_process_layout_browsertest.cc
@@ -441,21 +441,23 @@
 
   // Wait until dppx becomes 2 if the frame's dpr hasn't beeen updated
   // to 2 yet.
-  const char kScript[] =
-      "function sendDpr() "
-      "{window.domAutomationController.send(window.devicePixelRatio);}; "
-      "if (window.devicePixelRatio == 2) sendDpr();"
-      "window.matchMedia('screen and "
-      "(min-resolution: 2dppx)').addListener(function(e) { if (e.matches) { "
-      "sendDpr();}})";
+  const char kScript[] = R"(
+      new Promise(resolve => {
+        if (window.devicePixelRatio == 2)
+          resolve(window.devicePixelRatio);
+        window.matchMedia('screen and (min-resolution: 2dppx)')
+            .addListener(function(e) {
+          if (e.matches) {
+            resolve(window.devicePixelRatio);
+          }
+        });
+      });
+      )";
   // Make sure that both main frame and iframe are updated to 2x.
-  EXPECT_EQ(expected_dip_scale,
-            EvalJs(child, kScript, content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
-                .ExtractDouble());
+  EXPECT_EQ(expected_dip_scale, EvalJs(child, kScript).ExtractDouble());
 
-  EXPECT_EQ(expected_dip_scale, EvalJs(web_contents(), kScript,
-                                       content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
-                                    .ExtractDouble());
+  EXPECT_EQ(expected_dip_scale,
+            EvalJs(web_contents(), kScript).ExtractDouble());
 }
 
 #endif
@@ -2275,10 +2277,8 @@
   FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
 
   // Add an onmessage handler to the subframe to send back its width.
-  EXPECT_TRUE(ExecJs(root->child_at(0), R"(
-      window.addEventListener('message', function(event) {
-        domAutomationController.send(document.body.clientWidth);
-      });)"));
+  EXPECT_TRUE(ExecJs(root->child_at(0),
+                     WaitForMessageScript("document.body.clientWidth")));
 
   // Drop the visual properties ACKs from the child renderer.  To do this,
   // unsubscribe the child's RenderWidgetHost from its
@@ -2294,14 +2294,14 @@
 
   // Now, resize the subframe twice from the main frame and send it a
   // postMessage. The postMessage handler should see the second updated size.
-  EXPECT_EQ(700, EvalJs(root, R"(
+  EXPECT_TRUE(ExecJs(root, R"(
       var f = document.querySelector('iframe');
       f.width = 500;
       f.offsetTop; // force layout; this sends a resize IPC for width of 500.
       f.width = 700;
       f.offsetTop; // force layout; this sends a resize IPC for width of 700.
-      f.contentWindow.postMessage('foo', '*');)",
-                        EXECUTE_SCRIPT_USE_MANUAL_REPLY));
+      f.contentWindow.postMessage('foo', '*');)"));
+  EXPECT_EQ(700, EvalJs(root->child_at(0), "onMessagePromise"));
 }
 
 // This test verifies that when scrolling an OOPIF in a pinched-zoomed page,
diff --git a/content/browser/site_per_process_scroll_browsertest.cc b/content/browser/site_per_process_scroll_browsertest.cc
index c134d45..584faab 100644
--- a/content/browser/site_per_process_scroll_browsertest.cc
+++ b/content/browser/site_per_process_scroll_browsertest.cc
@@ -838,13 +838,7 @@
   key_event.SetType(blink::WebKeyboardEvent::Type::kKeyUp);
   rwhv_child->GetRenderWidgetHost()->ForwardKeyboardEvent(key_event);
 
-  double scrolled_y =
-      EvalJs(root,
-             "waitForScrollDownPromise.then((scrolled_y) => {"
-             "  window.domAutomationController.send(scrolled_y);"
-             "});",
-             content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
-          .ExtractDouble();
+  double scrolled_y = EvalJs(root, "waitForScrollDownPromise").ExtractDouble();
   EXPECT_GT(scrolled_y, 0.0);
 }
 
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index 934a957b..969ac09 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -1156,26 +1156,20 @@
 
 void RenderAccessibilityImpl::AddImageAnnotations(
     const WebDocument& document,
-    std::vector<ui::AXNodeData>& nodes) {
+    std::vector<ui::AXNodeData*>& nodes) {
   if (accessibility_mode_.has_mode(ui::AXMode::kPDF))
     return;
-  for (auto& node : nodes) {
-    WebAXObject src = WebAXObject::FromWebDocumentByID(document, node.id);
+  for (auto* node : nodes) {
+    WebAXObject src = WebAXObject::FromWebDocumentByID(document, node->id);
 
-    // This logic is equivalent to the early-outs in
-    // BlinkAXTreeSource::SerializeNode
-    if (src.IsDetached() || !src.AccessibilityIsIncludedInTree() ||
-        (src.AccessibilityIsIgnored() &&
-         !node.HasState(ax::mojom::State::kFocusable)))
-      continue;
-
-    if (ui::IsImage(node.role)) {
-      AddImageAnnotationsForNode(src, &node);
-    } else if ((ui::IsLink(node.role) || ui::IsPlatformDocument(node.role)) &&
-               node.GetNameFrom() != ax::mojom::NameFrom::kAttribute) {
+    if (ui::IsImage(node->role)) {
+      AddImageAnnotationsForNode(src, node);
+    } else {
+      DCHECK((ui::IsLink(node->role) || ui::IsPlatformDocument(node->role)) &&
+             node->GetNameFrom() != ax::mojom::NameFrom::kAttribute);
       WebAXObject inner_image;
       if (FindExactlyOneInnerImageInMaxDepthThree(src, &inner_image))
-        AddImageAnnotationsForNode(inner_image, &node);
+        AddImageAnnotationsForNode(inner_image, node);
     }
   }
 }
@@ -1214,7 +1208,9 @@
       AddPluginTreeToUpdate(&update, mark_plugin_subtree_dirty);
     }
 
-    AddImageAnnotations(document, update.nodes);
+    std::vector<ui::AXNodeData*> image_nodes;
+    ax_context_->GetImagesToAnnotate(update, image_nodes);
+    AddImageAnnotations(document, image_nodes);
   }
 
   if (had_end_of_test_event) {
diff --git a/content/renderer/accessibility/render_accessibility_impl.h b/content/renderer/accessibility/render_accessibility_impl.h
index e2a8e4be..595080b 100644
--- a/content/renderer/accessibility/render_accessibility_impl.h
+++ b/content/renderer/accessibility/render_accessibility_impl.h
@@ -261,7 +261,7 @@
                                  bool mark_plugin_subtree_dirty);
 
   void AddImageAnnotations(const blink::WebDocument& document,
-                           std::vector<ui::AXNodeData>&);
+                           std::vector<ui::AXNodeData*>&);
   void AddImageAnnotationsForNode(blink::WebAXObject& src, ui::AXNodeData* dst);
 
   static void IgnoreProtocolChecksForTesting();
diff --git a/content/test/content_unittests_bundle_data.filelist b/content/test/content_unittests_bundle_data.filelist
index dcd5f08..332c2f9 100644
--- a/content/test/content_unittests_bundle_data.filelist
+++ b/content/test/content_unittests_bundle_data.filelist
@@ -40,7 +40,6 @@
 data/attribution_reporting/aggregatable_report_goldens/version_/report_5_cleartext_payloads.json
 data/attribution_reporting/aggregatable_report_goldens/version_/report_6.json
 data/attribution_reporting/aggregatable_report_goldens/version_/report_6_cleartext_payloads.json
-data/attribution_reporting/databases/version_34.sql
 data/attribution_reporting/databases/version_35.sql
 data/attribution_reporting/databases/version_36.sql
 data/attribution_reporting/databases/version_37.sql
@@ -794,6 +793,8 @@
 data/gpu/webcodecs/encode-decode.html
 data/gpu/webcodecs/encode.html
 data/gpu/webcodecs/encoding-modes.html
+data/gpu/webcodecs/encoding-rate-control.html
+data/gpu/webcodecs/encoding-rate-control.js
 data/gpu/webcodecs/svc.html
 data/gpu/webcodecs/tex-image-2d.html
 data/gpu/webcodecs/webcodecs_common.js
diff --git a/content/test/data/attribution_reporting/databases/version_34.sql b/content/test/data/attribution_reporting/databases/version_34.sql
deleted file mode 100644
index a81bd0f2..0000000
--- a/content/test/data/attribution_reporting/databases/version_34.sql
+++ /dev/null
@@ -1,55 +0,0 @@
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE sources(source_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,source_event_id INTEGER NOT NULL,source_origin TEXT NOT NULL,destination_origin TEXT NOT NULL,reporting_origin TEXT NOT NULL,source_time INTEGER NOT NULL,expiry_time INTEGER NOT NULL,num_attributions INTEGER NOT NULL,event_level_active INTEGER NOT NULL,aggregatable_active INTEGER NOT NULL,destination_site TEXT NOT NULL,source_type INTEGER NOT NULL,attribution_logic INTEGER NOT NULL,priority INTEGER NOT NULL,source_site TEXT NOT NULL,debug_key INTEGER,aggregatable_budget_consumed INTEGER NOT NULL,aggregatable_source BLOB NOT NULL,filter_data BLOB NOT NULL);
-
-CREATE TABLE event_level_reports(report_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,source_id INTEGER NOT NULL,trigger_data INTEGER NOT NULL,trigger_time INTEGER NOT NULL,report_time INTEGER NOT NULL,priority INTEGER NOT NULL,failed_send_attempts INTEGER NOT NULL,external_report_id TEXT NOT NULL,debug_key INTEGER);
-
-CREATE TABLE rate_limits(id INTEGER PRIMARY KEY NOT NULL,scope INTEGER NOT NULL,source_id INTEGER NOT NULL,source_site TEXT NOT NULL,source_origin TEXT NOT NULL,destination_site TEXT NOT NULL,destination_origin TEXT NOT NULL,reporting_origin TEXT NOT NULL,time INTEGER NOT NULL);
-
-CREATE TABLE dedup_keys(source_id INTEGER NOT NULL,dedup_key INTEGER NOT NULL,PRIMARY KEY(source_id,dedup_key))WITHOUT ROWID;
-
-CREATE TABLE aggregatable_report_metadata(aggregation_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,source_id INTEGER NOT NULL,trigger_time INTEGER NOT NULL,debug_key INTEGER,external_report_id TEXT NOT NULL,report_time INTEGER NOT NULL,failed_send_attempts INTEGER NOT NULL,initial_report_time INTEGER NOT NULL);
-
-CREATE TABLE aggregatable_contributions(contribution_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,aggregation_id INTEGER NOT NULL,key_high_bits INTEGER NOT NULL,key_low_bits INTEGER NOT NULL,value INTEGER NOT NULL);
-
-CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-
-INSERT INTO meta VALUES('mmap_status','-1');
-INSERT INTO meta VALUES('version','34');
-INSERT INTO meta VALUES('last_compatible_version','34');
-
-CREATE INDEX sources_by_active_destination_site_reporting_origin ON sources(event_level_active,aggregatable_active,destination_site,reporting_origin);
-
-CREATE INDEX sources_by_expiry_time ON sources(expiry_time);
-
-CREATE INDEX sources_by_origin ON sources(source_origin);
-
-CREATE INDEX active_unattributed_sources_by_site_reporting_origin ON sources(source_site,reporting_origin)WHERE event_level_active=1 AND num_attributions=0 AND aggregatable_active=1 AND aggregatable_budget_consumed=0;
-
-CREATE INDEX event_level_reports_by_report_time ON event_level_reports(report_time);
-
-CREATE INDEX event_level_reports_by_source_id ON event_level_reports(source_id);
-
-CREATE INDEX rate_limit_attribution_idx ON rate_limits(destination_site,source_site,reporting_origin,time)WHERE scope=1;
-
-CREATE INDEX rate_limit_reporting_origin_idx ON rate_limits(scope,destination_site,source_site,time);
-
-CREATE INDEX rate_limit_time_idx ON rate_limits(time);
-
-CREATE INDEX rate_limit_source_id_idx ON rate_limits(source_id);
-
-CREATE INDEX aggregate_source_id_idx ON aggregatable_report_metadata(source_id);
-
-CREATE INDEX aggregate_trigger_time_idx ON aggregatable_report_metadata(trigger_time);
-
-CREATE INDEX aggregate_report_time_idx ON aggregatable_report_metadata(report_time);
-
-CREATE INDEX contribution_aggregation_id_idx ON aggregatable_contributions(aggregation_id);
-
-INSERT INTO sources VALUES(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19);
-
-INSERT INTO event_level_reports VALUES (0,1,2,3,4,5,6,7,8);
-
-COMMIT;
diff --git a/content/test/data/attribution_reporting/databases/version_35.sql b/content/test/data/attribution_reporting/databases/version_35.sql
index b2da41a..07d380d 100644
--- a/content/test/data/attribution_reporting/databases/version_35.sql
+++ b/content/test/data/attribution_reporting/databases/version_35.sql
@@ -48,4 +48,7 @@
 
 CREATE INDEX contribution_aggregation_id_idx ON aggregatable_contributions(aggregation_id);
 
+INSERT INTO sources VALUES(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19);
+INSERT INTO event_level_reports VALUES (0,1,2,3,4,5,6,7,8);
+
 COMMIT;
diff --git a/content/test/data/gpu/webcodecs/encoding-rate-control.html b/content/test/data/gpu/webcodecs/encoding-rate-control.html
new file mode 100644
index 0000000..21e6bdf
--- /dev/null
+++ b/content/test/data/gpu/webcodecs/encoding-rate-control.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!--
+Encodes several seconds of video and checks that the encoder adheres to the
+specified bitrate.
+
+Each frame has a number of new gradient filled ellipses appearing on it.
+We can control complexity of the video by changing the number of ellipses.
+It's easier on encoders than pure white noise, but not as easy as some static or
+a ffmpeg test input.
+-->
+<html>
+
+<head>
+  <title>Encoding bitrate test</title>
+  <script src="webcodecs_common.js"></script>
+  <script src="encoding-rate-control.js"></script>
+</head>
+
+<body>
+  <canvas id="src"></canvas>
+</body>
+
+</html>
diff --git a/content/test/data/gpu/webcodecs/encoding-rate-control.js b/content/test/data/gpu/webcodecs/encoding-rate-control.js
new file mode 100644
index 0000000..290220902
--- /dev/null
+++ b/content/test/data/gpu/webcodecs/encoding-rate-control.js
@@ -0,0 +1,139 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+function createRandomGenerator(seed) {
+  return function(max) {
+    seed = ((1 + seed) * 7 + 13) & 0x7fffffff;
+    return seed % max;
+  }
+}
+
+// Draw pseudorandom animation on the canvas
+function createDrawingFunction(cnv, changes_per_frame) {
+  var ctx = cnv.getContext('2d');
+  let width = cnv.width;
+  let height = cnv.height;
+  let rnd = createRandomGenerator(425533);
+  const w = 35;
+  const h = 30;
+  const white_noise_img = ctx.createImageData(w, h);
+  self.crypto.getRandomValues(white_noise_img.data);
+
+  return function() {
+    let x = 0, y = 0;
+    // Paint gradient filled ellipses
+    for (let i = 0; i < changes_per_frame; i++) {
+      x = rnd(width);
+      y = rnd(height);
+
+      let a =
+          'rgba(' + rnd(255) + ',' + rnd(255) + ',' + rnd(255) + ',' + 1 + ')';
+      let b =
+          'rgba(' + rnd(255) + ',' + rnd(255) + ',' + rnd(255) + ',' + 1 + ')';
+      let c =
+          'rgba(' + rnd(255) + ',' + rnd(255) + ',' + rnd(255) + ',' + 1 + ')';
+      let gradient = ctx.createLinearGradient(x, y, x + w, y + h);
+      gradient.addColorStop(0, a);
+      gradient.addColorStop(0.5, b);
+      gradient.addColorStop(1, c);
+
+      ctx.fillStyle = gradient;
+      ctx.beginPath();
+      ctx.ellipse(x, y, w / 2, h / 2, 0.0, 0.0, 2 * Math.PI);
+      ctx.fill();
+    }
+
+    // Add a bit of white noise
+    ctx.putImageData(white_noise_img, x, y);
+  }
+}
+
+async function main(arg) {
+  const width = 1280;
+  const height = 720;
+  const seconds = 5;
+  const fps = 30;
+  const frames_to_encode = fps * seconds;
+  let errors = 0;
+  let chunks = [];
+
+  const encoder_config = {
+    codec: arg.codec,
+    hardwareAcceleration: arg.acceleration,
+    width: width,
+    height: height,
+    bitrate: arg.bitrate,
+    bitrateMode: arg.bitrate_mode,
+    framerate: fps
+  };
+
+  let support = await VideoEncoder.isConfigSupported(encoder_config);
+  if (!support.supported) {
+    TEST.skip('Unsupported codec: ' + arg.codec);
+    return;
+  }
+
+  // Start drawing canvas animation that will be source of the frames
+  // for the test.
+  let cnv = document.getElementById('src');
+  cnv.width = width;
+  cnv.height = height;
+  const changes_per_frame = 25;
+  const draw = createDrawingFunction(cnv, changes_per_frame);
+
+  const init = {
+    output(chunk, metadata) {
+      chunks.push(chunk);
+    },
+    error(e) {
+      errors++;
+      TEST.log(e);
+    }
+  };
+
+  let encoder = new VideoEncoder(init);
+  encoder.configure(encoder_config);
+
+  // Take frames from the canvas and encoder them with a given bitrate
+  for (let i = 0; i < frames_to_encode; i++) {
+    const time_us = i / fps * 1_000_000;
+    draw();
+    let frame = new VideoFrame(cnv, {timestamp: time_us});
+    encoder.encode(frame, {keyFrame: false});
+    frame.close();
+    await waitForNextFrame();
+  }
+
+  await encoder.flush();
+  TEST.log('Encoding completed');
+  encoder.close();
+
+  TEST.assert(errors == 0, 'Encoding errors occurred during the test');
+  TEST.assert(
+      chunks.length == frames_to_encode,
+      'Output count mismatch: ' + chunks.length);
+
+  // Calculate total size of the encoded video
+  let total_encoded_size = 0;
+  for (let chunk of chunks) {
+    total_encoded_size += chunk.byteLength;
+  }
+
+  const actual_bitrate = total_encoded_size / seconds * 8 /* bits */;
+  const bitrate_mismatch = Math.abs(actual_bitrate - arg.bitrate) / arg.bitrate;
+
+  // Check how far off expected encoded size from the ideal, CBR is
+  // supposed to be more strict that VBR.
+  let tolerance = (arg.bitrate_mode == 'constant') ? 0.15 : 0.30;
+  TEST.assert(
+      bitrate_mismatch < tolerance,
+      `Bitrate is too far off. Expected ${arg.bitrate}` +
+          ` Actual bitrate: ${actual_bitrate}` +
+          ` Tolerance: ${tolerance * arg.bitrate}` +
+          ` Mismatch: ${bitrate_mismatch}`);
+
+  TEST.log('Test completed');
+}
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index f475c466..38e558b 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -97,6 +97,11 @@
 crbug.com/1409453 [ win amd ] WebCodecs_DrawImage_hw_decoder [ Failure ]
 crbug.com/1409453 [ win amd ] WebCodecs_TexImage2d_hw_decoder [ Failure ]
 
+# Not all platform encoders are very good at adhering to specified bitrate
+crbug.com/1368785 [ mac-x86_64 ] WebCodecs_EncodingRateControl_avc1.420034_prefer-hardware_constant_* [ Failure ]
+# Android seems to be especially bad at rate control, let's wait and see what actually works
+crbug.com/1368785 [ android ] WebCodecs_EncodingRateControl_* [ Failure ]
+
 # The following expectations are added to enable WebCodecs tests on Sherlock.
 # unexpected_pass_finder is disabled so that the tool does not automatically remove these expectations.
 # TODO(crbug.com/1400465) Enable unexpected_pass_finder once test is running regularly on Sherlock.
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 6158138..6dc949c 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -224,10 +224,13 @@
 crbug.com/1426916 [ chromeos chromeos-board-amd64-generic passthrough ] conformance/extensions/webgl-compressed-texture-astc.html [ Slow ]
 crbug.com/1426916 [ chromeos chromeos-board-amd64-generic passthrough ] conformance/extensions/s3tc-and-rgtc.html [ Slow ]
 crbug.com/1426916 [ chromeos chromeos-board-amd64-generic passthrough ] conformance/textures/canvas_sub_rectangle/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Slow ]
+crbug.com/1426916 [ chromeos chromeos-board-amd64-generic passthrough ] conformance/textures/misc/tex-image-and-sub-image-2d-with-array-buffer-view.html [ Slow ]
 crbug.com/1426916 [ chromeos chromeos-board-amd64-generic passthrough ] conformance2/extensions/webgl-multi-draw-instanced-base-vertex-base-instance.html [ Slow ]
+crbug.com/1426916 [ chromeos chromeos-board-amd64-generic passthrough ] conformance2/sync/sync-webgl-specific.html [ Slow ]
 crbug.com/1426916 [ chromeos chromeos-board-amd64-generic passthrough ] conformance2/textures/canvas_sub_rectangle/tex-2d-rg32f-rg-float.html [ Slow ]
 crbug.com/1426916 [ chromeos chromeos-board-amd64-generic passthrough ] conformance2/textures/canvas_sub_rectangle/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Slow ]
 crbug.com/1426916 [ chromeos chromeos-board-amd64-generic passthrough ] conformance2/uniforms/incompatible-texture-type-for-sampler.html [ Slow ]
+crbug.com/1426916 [ chromeos chromeos-board-amd64-generic passthrough ] deqp/functional/gles3/uniformapi/random.html [ Slow ]
 
 ###################
 # Failures/Flakes #
@@ -749,8 +752,6 @@
 crbug.com/1241183 [ chromeos chromeos-board-amd64-generic passthrough ] conformance2/textures/misc/immutable-tex-render-feedback.html [ Failure ]
 crbug.com/1399117 [ chromeos chromeos-board-amd64-generic passthrough ] WebglExtension_WEBGL_provoking_vertex [ Skip ]
 
-crbug.com/1426913 [ chromeos chromeos-board-amd64-generic passthrough ] conformance2/sync/sync-webgl-specific.html [ RetryOnFailure ]
-
 # Must investigate ChromeOS failures with passthrough command decoder.
 crbug.com/angleproject/5038 [ chromeos passthrough ] conformance/extensions/ext-color-buffer-half-float.html [ Failure ]
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 0c593354..9d6b907 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -335,6 +335,8 @@
 crbug.com/1426916 [ chromeos chromeos-board-amd64-generic no-passthrough ] conformance/extensions/webgl-compressed-texture-astc.html [ Slow ]
 crbug.com/1426916 [ chromeos chromeos-board-amd64-generic no-passthrough ] conformance/reading/read-pixels-test.html [ Slow ]
 crbug.com/1426916 [ chromeos chromeos-board-amd64-generic no-passthrough ] conformance/textures/misc/tex-image-and-sub-image-2d-with-array-buffer-view.html [ Slow ]
+crbug.com/1427243 [ fuchsia fuchsia-board-astro ] conformance/glsl/bugs/complex-glsl-does-not-crash.html [ Slow ]
+crbug.com/1427243 [ fuchsia fuchsia-board-sherlock ] conformance/glsl/bugs/complex-glsl-does-not-crash.html [ Slow ]
 
 ###################
 # Failures/Flakes #
diff --git a/content/test/gpu/gpu_tests/webcodecs_integration_test.py b/content/test/gpu/gpu_tests/webcodecs_integration_test.py
index 48510a3..d281b16 100644
--- a/content/test/gpu/gpu_tests/webcodecs_integration_test.py
+++ b/content/test/gpu/gpu_tests/webcodecs_integration_test.py
@@ -37,7 +37,7 @@
   @classmethod
   def GenerateGpuTests(cls, options: ct.ParsedCmdArgs) -> ct.TestGenerator:
     tests = itertools.chain(cls.GenerateFrameTests(), cls.GenerateVideoTests(),
-                            cls.GenerateAudioTests())
+                            cls.GenerateAudioTests(), cls.BitrateTests())
     for test in tests:
       yield test
 
@@ -81,6 +81,22 @@
     }])
 
   @classmethod
+  def BitrateTests(cls) -> ct.TestGenerator:
+    high_res_codecs = ['avc1.420034', 'vp8', 'vp09.00.10.08', 'av01.0.04M.08']
+    for codec in high_res_codecs:
+      for acc in accelerations:
+        for bitrate_mode in ['constant', 'variable']:
+          for bitrate in [1500000, 2000000, 3000000]:
+            args = (codec, acc, bitrate_mode, bitrate)
+            yield ('WebCodecs_EncodingRateControl_%s_%s_%s_%s' % args,
+                   'encoding-rate-control.html', [{
+                       'codec': codec,
+                       'acceleration': acc,
+                       'bitrate_mode': bitrate_mode,
+                       'bitrate': bitrate
+                   }])
+
+  @classmethod
   def GenerateVideoTests(cls) -> ct.TestGenerator:
     yield ('WebCodecs_WebRTCPeerConnection_Window',
            'webrtc-peer-connection.html', [{
diff --git a/device/bluetooth/floss/bluetooth_gatt_service_floss.cc b/device/bluetooth/floss/bluetooth_gatt_service_floss.cc
index b7381431..4f23344a 100644
--- a/device/bluetooth/floss/bluetooth_gatt_service_floss.cc
+++ b/device/bluetooth/floss/bluetooth_gatt_service_floss.cc
@@ -25,10 +25,12 @@
     BluetoothAdapterFloss* adapter)
     : adapter_(adapter) {
   FlossDBusManager::Get()->GetGattManagerClient()->AddObserver(this);
+  FlossDBusManager::Get()->GetGattManagerClient()->AddServerObserver(this);
 }
 
 BluetoothGattServiceFloss::~BluetoothGattServiceFloss() {
   FlossDBusManager::Get()->GetGattManagerClient()->RemoveObserver(this);
+  FlossDBusManager::Get()->GetGattManagerClient()->RemoveServerObserver(this);
 }
 
 BluetoothAdapterFloss* BluetoothGattServiceFloss::GetAdapter() const {
@@ -59,12 +61,31 @@
     observer_by_handle_[handle] = observer;
 }
 
+void BluetoothGattServiceFloss::AddServerObserverForHandle(
+    int32_t handle,
+    FlossGattServerObserver* observer) {
+  DCHECK(!base::Contains(server_observer_by_handle_, handle));
+  DCHECK(observer);
+
+  if (observer) {
+    server_observer_by_handle_[handle] = observer;
+  }
+}
+
 void BluetoothGattServiceFloss::RemoveObserverForHandle(int32_t handle) {
   DCHECK(base::Contains(observer_by_handle_, handle));
 
   observer_by_handle_.erase(handle);
 }
 
+void BluetoothGattServiceFloss::RemoveServerObserverForHandle(int32_t handle) {
+  if (!base::Contains(server_observer_by_handle_, handle)) {
+    return;
+  }
+
+  server_observer_by_handle_.erase(handle);
+}
+
 void BluetoothGattServiceFloss::GattCharacteristicRead(
     std::string address,
     GattStatus status,
@@ -111,4 +132,60 @@
   }
 }
 
+void BluetoothGattServiceFloss::GattServerCharacteristicReadRequest(
+    std::string address,
+    int32_t request_id,
+    int32_t offset,
+    bool is_long,
+    int32_t handle) {
+  if (base::Contains(server_observer_by_handle_, handle)) {
+    server_observer_by_handle_[handle]->GattServerCharacteristicReadRequest(
+        address, request_id, offset, is_long, handle);
+  }
+}
+
+void BluetoothGattServiceFloss::GattServerDescriptorReadRequest(
+    std::string address,
+    int32_t request_id,
+    int32_t offset,
+    bool is_long,
+    int32_t handle) {
+  if (base::Contains(server_observer_by_handle_, handle)) {
+    server_observer_by_handle_[handle]->GattServerDescriptorReadRequest(
+        address, request_id, offset, is_long, handle);
+  }
+}
+
+void BluetoothGattServiceFloss::GattServerCharacteristicWriteRequest(
+    std::string address,
+    int32_t request_id,
+    int32_t offset,
+    int32_t length,
+    bool is_prepared_write,
+    bool needs_response,
+    int32_t handle,
+    std::vector<uint8_t> value) {
+  if (base::Contains(server_observer_by_handle_, handle)) {
+    server_observer_by_handle_[handle]->GattServerCharacteristicWriteRequest(
+        address, request_id, offset, length, is_prepared_write, needs_response,
+        handle, value);
+  }
+}
+
+void BluetoothGattServiceFloss::GattServerDescriptorWriteRequest(
+    std::string address,
+    int32_t request_id,
+    int32_t offset,
+    int32_t length,
+    bool is_prepared_write,
+    bool needs_response,
+    int32_t handle,
+    std::vector<uint8_t> value) {
+  if (base::Contains(server_observer_by_handle_, handle)) {
+    server_observer_by_handle_[handle]->GattServerDescriptorWriteRequest(
+        address, request_id, offset, length, is_prepared_write, needs_response,
+        handle, value);
+  }
+}
+
 }  // namespace floss
diff --git a/device/bluetooth/floss/bluetooth_gatt_service_floss.h b/device/bluetooth/floss/bluetooth_gatt_service_floss.h
index 0f02e26..afbf8b9 100644
--- a/device/bluetooth/floss/bluetooth_gatt_service_floss.h
+++ b/device/bluetooth/floss/bluetooth_gatt_service_floss.h
@@ -16,7 +16,8 @@
 // Subclass of |BluetoothGattService| for platforms that use Floss.
 class DEVICE_BLUETOOTH_EXPORT BluetoothGattServiceFloss
     : public device::BluetoothGattService,
-      public FlossGattClientObserver {
+      public FlossGattClientObserver,
+      public FlossGattServerObserver {
  public:
   BluetoothGattServiceFloss(const BluetoothGattServiceFloss&) = delete;
   BluetoothGattServiceFloss& operator=(const BluetoothGattServiceFloss&) =
@@ -32,9 +33,12 @@
   // Adds an observer for a specific handle. This observer will only get
   // callbacks invoked for that specific handle.
   void AddObserverForHandle(int32_t handle, FlossGattClientObserver* observer);
+  void AddServerObserverForHandle(int32_t handle,
+                                  FlossGattServerObserver* observer);
 
   // Removes the observer for a specific handle.
   void RemoveObserverForHandle(int32_t handle);
+  void RemoveServerObserverForHandle(int32_t handle);
 
   // FlossGattClientObserver overrides.
   void GattCharacteristicRead(std::string address,
@@ -55,6 +59,35 @@
                   int32_t handle,
                   const std::vector<uint8_t>& data) override;
 
+  // FlossGattServerObserver overrides.
+  void GattServerCharacteristicReadRequest(std::string address,
+                                           int32_t request_id,
+                                           int32_t offset,
+                                           bool is_long,
+                                           int32_t handle) override;
+  void GattServerDescriptorReadRequest(std::string address,
+                                       int32_t request_id,
+                                       int32_t offset,
+                                       bool is_long,
+                                       int32_t handle) override;
+  void GattServerCharacteristicWriteRequest(
+      std::string address,
+      int32_t request_id,
+      int32_t offset,
+      int32_t length,
+      bool is_prepared_write,
+      bool needs_response,
+      int32_t handle,
+      std::vector<uint8_t> value) override;
+  void GattServerDescriptorWriteRequest(std::string address,
+                                        int32_t request_id,
+                                        int32_t offset,
+                                        int32_t length,
+                                        bool is_prepared_write,
+                                        bool needs_response,
+                                        int32_t handle,
+                                        std::vector<uint8_t> value) override;
+
  protected:
   explicit BluetoothGattServiceFloss(BluetoothAdapterFloss* adapter);
   ~BluetoothGattServiceFloss() override;
@@ -63,6 +96,8 @@
   // for a specific handle within this GATT service, it is dispatched here to
   // that specific observer.
   std::map<int32_t, raw_ptr<FlossGattClientObserver>> observer_by_handle_;
+  std::map<int32_t, raw_ptr<FlossGattServerObserver>>
+      server_observer_by_handle_;
 
  private:
   // The adapter associated with (and which indirectly owns) this service.
diff --git a/device/bluetooth/floss/floss_gatt_manager_client.cc b/device/bluetooth/floss/floss_gatt_manager_client.cc
index f7fbb99c..fd8df95 100644
--- a/device/bluetooth/floss/floss_gatt_manager_client.cc
+++ b/device/bluetooth/floss/floss_gatt_manager_client.cc
@@ -273,7 +273,8 @@
   gatt_client_observers_.AddObserver(observer);
 }
 
-void FlossGattManagerClient::AddObserver(FlossGattServerObserver* observer) {
+void FlossGattManagerClient::AddServerObserver(
+    FlossGattServerObserver* observer) {
   gatt_server_observers_.AddObserver(observer);
 }
 
@@ -281,7 +282,8 @@
   gatt_client_observers_.RemoveObserver(observer);
 }
 
-void FlossGattManagerClient::RemoveObserver(FlossGattServerObserver* observer) {
+void FlossGattManagerClient::RemoveServerObserver(
+    FlossGattServerObserver* observer) {
   gatt_server_observers_.RemoveObserver(observer);
 }
 
diff --git a/device/bluetooth/floss/floss_gatt_manager_client.h b/device/bluetooth/floss/floss_gatt_manager_client.h
index 22b6382..ac55dac5 100644
--- a/device/bluetooth/floss/floss_gatt_manager_client.h
+++ b/device/bluetooth/floss/floss_gatt_manager_client.h
@@ -369,9 +369,9 @@
 
   // Manage observers.
   void AddObserver(FlossGattClientObserver* observer);
-  void AddObserver(FlossGattServerObserver* observer);
+  void AddServerObserver(FlossGattServerObserver* observer);
   void RemoveObserver(FlossGattClientObserver* observer);
-  void RemoveObserver(FlossGattServerObserver* observer);
+  void RemoveServerObserver(FlossGattServerObserver* observer);
 
   // TODO(@sarveshkalwit): Rename client functions, ex. Connect->ClientConnect
   // Create a GATT client connection to a remote device on given transport.
diff --git a/extensions/browser/api/runtime/runtime_api.cc b/extensions/browser/api/runtime/runtime_api.cc
index e0ad18ab..d112f7e 100644
--- a/extensions/browser/api/runtime/runtime_api.cc
+++ b/extensions/browser/api/runtime/runtime_api.cc
@@ -33,6 +33,7 @@
 #include "extensions/browser/lazy_context_task_queue.h"
 #include "extensions/browser/process_manager_factory.h"
 #include "extensions/common/api/runtime.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest_handlers/background_info.h"
 #include "extensions/common/manifest_handlers/shared_module_info.h"
@@ -744,4 +745,46 @@
   return RespondNow(WithArguments(std::move(dict)));
 }
 
+RuntimeGetContextsFunction::RuntimeGetContextsFunction() = default;
+RuntimeGetContextsFunction::~RuntimeGetContextsFunction() = default;
+
+ExtensionFunction::ResponseAction RuntimeGetContextsFunction::Run() {
+  EXTENSION_FUNCTION_VALIDATE(extension());
+
+  std::vector<api::runtime::ExtensionContext> result;
+  if (absl::optional<api::runtime::ExtensionContext> worker =
+          GetWorkerContext()) {
+    result.push_back(std::move(*worker));
+  }
+
+  return RespondNow(
+      ArgumentList(api::runtime::GetContexts::Results::Create(result)));
+}
+
+absl::optional<api::runtime::ExtensionContext>
+RuntimeGetContextsFunction::GetWorkerContext() {
+  ProcessManager* const process_manager =
+      ProcessManager::Get(browser_context());
+  DCHECK(process_manager);
+
+  std::vector<WorkerId> active_workers =
+      process_manager->GetServiceWorkersForExtension(extension()->id());
+  CHECK_LE(active_workers.size(), 1u);
+
+  if (active_workers.empty()) {
+    return absl::nullopt;
+  }
+
+  api::runtime::ExtensionContext context;
+  context.context_type = api::runtime::CONTEXT_TYPE_BACKGROUND;
+  // TODO(crbug/1426192): Add a real context id.
+  context.context_id = "";
+  context.tab_id = extension_misc::kUnknownTabId;
+  context.window_id = extension_misc::kUnknownWindowId;
+  // TODO(devlin): Add extension_misc::kUnknownFrameId and use it here?
+  context.frame_id = -1;
+  context.incognito = browser_context()->IsOffTheRecord();
+  return context;
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/api/runtime/runtime_api.h b/extensions/browser/api/runtime/runtime_api.h
index 8845c8d..daa15c0f 100644
--- a/extensions/browser/api/runtime/runtime_api.h
+++ b/extensions/browser/api/runtime/runtime_api.h
@@ -25,6 +25,7 @@
 #include "extensions/browser/process_manager_observer.h"
 #include "extensions/browser/update_observer.h"
 #include "extensions/common/api/runtime.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class Version;
@@ -319,6 +320,23 @@
   ResponseAction Run() override;
 };
 
+class RuntimeGetContextsFunction : public ExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("runtime.getContexts", RUNTIME_GETCONTEXTS)
+
+  RuntimeGetContextsFunction();
+  RuntimeGetContextsFunction(const RuntimeGetContextsFunction&) = delete;
+  RuntimeGetContextsFunction& operator=(const RuntimeGetContextsFunction&) =
+      delete;
+
+ private:
+  // ExtensionFunction:
+  ~RuntimeGetContextsFunction() override;
+  ResponseAction Run() override;
+
+  absl::optional<api::runtime::ExtensionContext> GetWorkerContext();
+};
+
 }  // namespace extensions
 
 #endif  // EXTENSIONS_BROWSER_API_RUNTIME_RUNTIME_API_H_
diff --git a/extensions/browser/browsertest_util.h b/extensions/browser/browsertest_util.h
index edcb7b5b..797b83a 100644
--- a/extensions/browser/browsertest_util.h
+++ b/extensions/browser/browsertest_util.h
@@ -37,7 +37,7 @@
     const std::string& extension_id,
     const std::string& script,
     ScriptUserActivation script_user_activation =
-        ScriptUserActivation::kActivate);
+        ScriptUserActivation::kDontActivate);
 
 // Same as ExecuteScriptInBackgroundPage, but doesn't wait for the script
 // to return a result. Fails the test and returns false if |extension_id|
@@ -58,7 +58,7 @@
     const std::string& extension_id,
     const std::string& script,
     ScriptUserActivation script_user_activation =
-        ScriptUserActivation::kActivate);
+        ScriptUserActivation::kDontActivate);
 
 // Synchronously stops the service worker registered by the extension with the
 // given `extension_id` at global scope. The extension must be installed and
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index a455a73..2fb379a 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1837,6 +1837,7 @@
   PASSWORDSPRIVATE_CONTINUEIMPORT = 1774,
   PASSWORDSPRIVATE_RESETIMPORTER = 1775,
   SMARTCARDPROVIDERPRIVATE_REPORTCANCELRESULT = 1776,
+  RUNTIME_GETCONTEXTS = 1777,
   // Last entry: Add new entries above, then run:
   // tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index cb65bb9..4c990575 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -543,6 +543,12 @@
     "dependencies": ["permission:nativeMessaging"],
     "contexts": ["blessed_extension"]
   },
+  "runtime.getContexts": {
+    "min_manifest_version": 3,
+    "contexts": ["blessed_extension"],
+    "channel": "trunk",
+    "feature_flag": "ApiRuntimeGetContexts"
+  },
   "runtime.getURL": {
     "contexts": [
       "blessed_extension",
diff --git a/extensions/common/api/runtime.json b/extensions/common/api/runtime.json
index b43e0dd..13f0b63 100644
--- a/extensions/common/api/runtime.json
+++ b/extensions/common/api/runtime.json
@@ -137,6 +137,57 @@
         "type": "string",
         "description": "The reason that the event is being dispatched. 'app_update' is used when the restart is needed because the application is updated to a newer version. 'os_update' is used when the restart is needed because the browser/OS is updated to a newer version. 'periodic' is used when the system runs for more than the permitted uptime set in the enterprise policy.",
         "enum": ["app_update", "os_update", "periodic"]
+      },
+      {
+        "id": "ContextType",
+        "type": "string",
+        "enum": ["TAB", "POPUP", "BACKGROUND", "OFFSCREEN_DOCUMENT"]
+      },
+      {
+        "id": "ExtensionContext",
+        "type": "object",
+        "description": "A context hosting extension content.",
+        "properties": {
+          "contextType": {
+            "$ref": "ContextType",
+            "description": "The type of context this corresponds to."
+          },
+          "contextId": {
+            "type": "string",
+            "description": "A unique identifier for this context"
+          },
+          "tabId": {
+            "type": "integer",
+            "description": "The ID of the tab for this context, or -1 if this context is not hosted in a tab."
+          },
+          "windowId": {
+            "type": "integer",
+            "description": "The ID of the window for this context, or -1 if this context is not hosted in a window."
+          },
+          "documentId": {
+            "type": "string",
+            "optional": true,
+            "description": "A UUID for the document associated with this context, or undefined if this context is hosted not in a document."
+          },
+          "frameId": {
+            "type": "integer",
+            "description": "The ID of the frame for this context, or -1 if this context is not hosted in a frame."
+          },
+          "documentUrl": {
+            "type": "string",
+            "optional": true,
+            "description": "The URL of the document associated with this context, or undefined if the context is not hosted in a document."
+          },
+          "documentOrigin": {
+            "type": "string",
+            "optional": true,
+            "description": "The origin of the document associated with this context, or undefined if the context is not hosted in a document."
+          },
+          "incognito": {
+            "type": "boolean",
+            "description": "Whether the context is associated with an incognito profile."
+          }
+        }
       }
     ],
     "properties": {
@@ -434,6 +485,26 @@
             ]
           }
         ]
+      },
+      {
+        "name": "getContexts",
+        "type": "function",
+        "description": "Fetches information about active contexts associated with this extension",
+        "parameters": [
+            // TODO(crbug/1426192): Add a filter.
+        ],
+        "returns_async": {
+          "name": "callback",
+          "description": "Invoked with the matching contexts, if any.",
+          "parameters": [
+            {
+              "name": "contexts",
+              "type": "array",
+              "description": "The matching contexts, if any.",
+              "items": { "$ref": "ExtensionContext" }
+            }
+          ]
+        }
       }
     ],
     "events": [
diff --git a/extensions/common/extension_features.cc b/extensions/common/extension_features.cc
index 7bea9dd4..d87af4c7 100644
--- a/extensions/common/extension_features.cc
+++ b/extensions/common/extension_features.cc
@@ -7,43 +7,20 @@
 
 namespace extensions_features {
 
-// Controls whether we show an install friction dialog when an Enhanced Safe
-// Browsing user tries to install an extension that is not included in the
-// Safe Browsing CRX allowlist. This feature also controls if we show a warning
-// in 'chrome://extensions' for extensions not included in the allowlist.
-BASE_FEATURE(kSafeBrowsingCrxAllowlistShowWarnings,
-             "SafeBrowsingCrxAllowlistShowWarnings",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+///////////////////////////////////////////////////////////////////////////////
+// API Features
+///////////////////////////////////////////////////////////////////////////////
 
-// Automatically disable extensions not included in the Safe Browsing CRX
-// allowlist if the user has turned on Enhanced Safe Browsing (ESB). The
-// extensions can be disabled at ESB opt-in time or when an extension is moved
-// out of the allowlist.
-BASE_FEATURE(kSafeBrowsingCrxAllowlistAutoDisable,
-             "SafeBrowsingCrxAllowlistAutoDisable",
+// Controls the availability of the runtime.getContexts() API.
+BASE_FEATURE(kApiRuntimeGetContexts,
+             "ApiRuntimeGetContexts",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Forces requests to go through WebRequestProxyingURLLoaderFactory.
-BASE_FEATURE(kForceWebRequestProxyForTest,
-             "ForceWebRequestProxyForTest",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+///////////////////////////////////////////////////////////////////////////////
+// Other Features
+///////////////////////////////////////////////////////////////////////////////
 
-// Enables the UI in the install prompt which lets a user choose to withhold
-// requested host permissions by default.
-BASE_FEATURE(kAllowWithholdingExtensionPermissionsOnInstall,
-             "AllowWithholdingExtensionPermissionsOnInstall",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
-// Enables support for the "match_origin_as_fallback" property in content
-// scripts.
-BASE_FEATURE(kContentScriptsMatchOriginAsFallback,
-             "ContentScriptsMatchOriginAsFallback",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-// Reports Extensions.WebRequest.KeepaliveRequestFinished when enabled.
-BASE_FEATURE(kReportKeepaliveUkm,
-             "ReportKeepaliveUkm",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+// For historical reasons, this includes some APIs. Please don't add more.
 
 // Whether extension contexts can use SharedArrayBuffers unconditionally (i.e.
 // without requiring cross origin isolation).
@@ -52,18 +29,18 @@
              "AllowSharedArrayBuffersUnconditionally",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// When enabled, causes Manifest V3 (and greater) extensions to use structured
-// cloning (instead of JSON serialization) for extension messaging, except when
-// communicating with native messaging hosts.
-BASE_FEATURE(kStructuredCloningForMV3Messaging,
-             "StructuredCloningForMV3Messaging",
+// Enables the UI in the install prompt which lets a user choose to withhold
+// requested host permissions by default.
+BASE_FEATURE(kAllowWithholdingExtensionPermissionsOnInstall,
+             "AllowWithholdingExtensionPermissionsOnInstall",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// When enabled, causes extensions to allow access to certain APIs only if the
-// user is in the developer mode.
-BASE_FEATURE(kRestrictDeveloperModeAPIs,
-             "RestrictDeveloperModeAPIs",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+// If enabled, calls RenderFrame::SetAllowsCrossBrowsingInstanceFrameLookup() in
+// DidCreateScriptContext() instead of DidCommitProvisionalLoad() to avoid
+// creating the script context too early which can be bad for performance.
+BASE_FEATURE(kAvoidEarlyExtensionScriptContextCreation,
+             "AvoidEarlyExtensionScriptContextCreation",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // When enabled, then bad_message::ReceivedBadMessage will be called when
 // browser receives an IPC from a content script and the IPC that unexpectedly
@@ -74,9 +51,10 @@
              "EMF_NO_EXTENSION_ID_FOR_EXTENSION_SOURCE",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Controls whether extensions can use the new favicon fetching in Manifest V3.
-BASE_FEATURE(kNewExtensionFaviconHandling,
-             "ExtensionsNewFaviconHandling",
+// Enables support for the "match_origin_as_fallback" property in content
+// scripts.
+BASE_FEATURE(kContentScriptsMatchOriginAsFallback,
+             "ContentScriptsMatchOriginAsFallback",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Determine if dynamic extension URLs are handled and redirected.
@@ -84,52 +62,20 @@
              "ExtensionDynamicURLRedirection",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enables enhanced site control for extensions and allowing the user to control
-// site permissions.
-BASE_FEATURE(kExtensionsMenuAccessControl,
-             "ExtensionsMenuAccessControl",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
-// If enabled, calls RenderFrame::SetAllowsCrossBrowsingInstanceFrameLookup() in
-// DidCreateScriptContext() instead of DidCommitProvisionalLoad() to avoid
-// creating the script context too early which can be bad for performance.
-BASE_FEATURE(kAvoidEarlyExtensionScriptContextCreation,
-             "AvoidEarlyExtensionScriptContextCreation",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-// The feature enabling offscreen documents in Manifest V3 extensions.
-BASE_FEATURE(kExtensionsOffscreenDocuments,
-             "ExtensionsOffscreenDocuments",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-// If enabled, allows APIs used by the webstore to be exposed on the URL for the
-// new webstore.
-BASE_FEATURE(kNewWebstoreDomain,
-             "NewWebstoreDomain",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Side panel API availability.
 BASE_FEATURE(kExtensionSidePanelIntegration,
              "ExtensionSidePanelIntegration",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// File Handlers.
-BASE_FEATURE(kExtensionWebFileHandlers,
-             "ExtensionWebFileHandlers",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // IsValidSourceUrl enforcement for ExtensionHostMsg_OpenChannelToExtension IPC.
 BASE_FEATURE(kExtensionSourceUrlEnforcement,
              "ExtensionSourceUrlEnforcement",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Controls the <webview> tag behaviour changes proposed as part of the guest
-// view MPArch migration. See
-// https://docs.google.com/document/d/1RVbtvklXUg9QCNvMT0r-1qDwJNeQFGoTCOD1Ur9mDa4/edit?usp=sharing
-// for details.
-BASE_FEATURE(kWebviewTagMPArchBehavior,
-             "WebviewTagMPArchBehavior",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+// File Handlers.
+BASE_FEATURE(kExtensionWebFileHandlers,
+             "ExtensionWebFileHandlers",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // If enabled, only manifest v3 extensions is allowed while v2 will be disabled.
 // Note that this feature is now only checked by `ExtensionManagement` which
@@ -140,18 +86,10 @@
              "ExtensionsManifestV3Only",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// If enabled, the minimum MV3 Content-Security-Policy will include
-// 'inline-speculation-rules' source in the script-src.
-// See https://crbug.com/1382361 to track the launch status.
-BASE_FEATURE(kMinimumMV3CSPWithInlineSpeculationRules,
-             "MinimumMV3CSPWithInlineSpeculationRules",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-// If enabled, APIs of the Telemetry Extension platform that have pending
-// approval will be enabled. Read more about the platform here:
-// https://chromium.googlesource.com/chromium/src/+/master/docs/telemetry_extension/README.md.
-BASE_FEATURE(kTelemetryExtensionPendingApprovalApi,
-             "TelemetryExtensionPendingApprovalApi",
+// Enables enhanced site control for extensions and allowing the user to control
+// site permissions.
+BASE_FEATURE(kExtensionsMenuAccessControl,
+             "ExtensionsMenuAccessControl",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
 // If enabled, user permitted sites are granted access. This should only happen
@@ -161,4 +99,81 @@
              "ExtensionsMenuAccessControlWithPermittedSitesName",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// The feature enabling offscreen documents in Manifest V3 extensions.
+BASE_FEATURE(kExtensionsOffscreenDocuments,
+             "ExtensionsOffscreenDocuments",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+// Forces requests to go through WebRequestProxyingURLLoaderFactory.
+BASE_FEATURE(kForceWebRequestProxyForTest,
+             "ForceWebRequestProxyForTest",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+// If enabled, the minimum MV3 Content-Security-Policy will include
+// 'inline-speculation-rules' source in the script-src.
+// See https://crbug.com/1382361 to track the launch status.
+BASE_FEATURE(kMinimumMV3CSPWithInlineSpeculationRules,
+             "MinimumMV3CSPWithInlineSpeculationRules",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+// Controls whether extensions can use the new favicon fetching in Manifest V3.
+BASE_FEATURE(kNewExtensionFaviconHandling,
+             "ExtensionsNewFaviconHandling",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+// If enabled, allows APIs used by the webstore to be exposed on the URL for the
+// new webstore.
+BASE_FEATURE(kNewWebstoreDomain,
+             "NewWebstoreDomain",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+BASE_FEATURE(kReportKeepaliveUkm,
+             "ReportKeepaliveUkm",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+// When enabled, causes extensions to allow access to certain APIs only if the
+// user is in the developer mode.
+BASE_FEATURE(kRestrictDeveloperModeAPIs,
+             "RestrictDeveloperModeAPIs",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+// Reports Extensions.WebRequest.KeepaliveRequestFinished when enabled.
+// Automatically disable extensions not included in the Safe Browsing CRX
+// allowlist if the user has turned on Enhanced Safe Browsing (ESB). The
+// extensions can be disabled at ESB opt-in time or when an extension is moved
+// out of the allowlist.
+BASE_FEATURE(kSafeBrowsingCrxAllowlistAutoDisable,
+             "SafeBrowsingCrxAllowlistAutoDisable",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+// Controls whether we show an install friction dialog when an Enhanced Safe
+// Browsing user tries to install an extension that is not included in the
+// Safe Browsing CRX allowlist. This feature also controls if we show a warning
+// in 'chrome://extensions' for extensions not included in the allowlist.
+BASE_FEATURE(kSafeBrowsingCrxAllowlistShowWarnings,
+             "SafeBrowsingCrxAllowlistShowWarnings",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+// When enabled, causes Manifest V3 (and greater) extensions to use structured
+// cloning (instead of JSON serialization) for extension messaging, except when
+// communicating with native messaging hosts.
+BASE_FEATURE(kStructuredCloningForMV3Messaging,
+             "StructuredCloningForMV3Messaging",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+// If enabled, APIs of the Telemetry Extension platform that have pending
+// approval will be enabled. Read more about the platform here:
+// https://chromium.googlesource.com/chromium/src/+/master/docs/telemetry_extension/README.md.
+BASE_FEATURE(kTelemetryExtensionPendingApprovalApi,
+             "TelemetryExtensionPendingApprovalApi",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+// Controls the <webview> tag behaviour changes proposed as part of the guest
+// view MPArch migration. See
+// https://docs.google.com/document/d/1RVbtvklXUg9QCNvMT0r-1qDwJNeQFGoTCOD1Ur9mDa4/edit?usp=sharing
+// for details.
+BASE_FEATURE(kWebviewTagMPArchBehavior,
+             "WebviewTagMPArchBehavior",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 }  // namespace extensions_features
diff --git a/extensions/common/extension_features.h b/extensions/common/extension_features.h
index f71242d..376e089a 100644
--- a/extensions/common/extension_features.h
+++ b/extensions/common/extension_features.h
@@ -9,52 +9,92 @@
 
 namespace extensions_features {
 
-BASE_DECLARE_FEATURE(kSafeBrowsingCrxAllowlistShowWarnings);
-BASE_DECLARE_FEATURE(kSafeBrowsingCrxAllowlistAutoDisable);
+///////////////////////////////////////////////////////////////////////////////
+// README!
+// * Please keep these features alphabetized. One exception: API features go
+//   at the top so that they are visibly grouped together.
+// * Adding a new feature for an extension API? Great!
+//   Please use the naming style `kApi<Namespace><Method>`, e.g.
+//   `kApiTabsCreate`.
+//   Note that if you are using the features.json files to restrict your
+//   API with the feature (which is usually best practice if you are introducing
+//   any new features), you will also have to add the feature entry to the list
+//   in extensions/common/features/feature_flags.cc so the features system can
+//   detect it.
+// * Naming Tips: Even though this file is unique to extensions, base::Features
+//   have to be globally unique. Thus, it's often best to give features very
+//   specific names (often including "Extension", unlike many C++ class names)
+//   since namespacing doesn't otherwise exist.
+///////////////////////////////////////////////////////////////////////////////
 
-BASE_DECLARE_FEATURE(kForceWebRequestProxyForTest);
+///////////////////////////////////////////////////////////////////////////////
+// API Features
+///////////////////////////////////////////////////////////////////////////////
 
-BASE_DECLARE_FEATURE(kAllowWithholdingExtensionPermissionsOnInstall);
+// NOTE(devlin): If there are consistently enough of these in flux, it might
+// make sense to have their own file.
 
-BASE_DECLARE_FEATURE(kContentScriptsMatchOriginAsFallback);
+BASE_DECLARE_FEATURE(kApiRuntimeGetContexts);
 
-BASE_DECLARE_FEATURE(kReportKeepaliveUkm);
+///////////////////////////////////////////////////////////////////////////////
+// Other Features
+///////////////////////////////////////////////////////////////////////////////
+
+// For historical reasons, this includes some APIs. Please don't add more.
 
 BASE_DECLARE_FEATURE(kAllowSharedArrayBuffersUnconditionally);
 
-BASE_DECLARE_FEATURE(kStructuredCloningForMV3Messaging);
-
-BASE_DECLARE_FEATURE(kRestrictDeveloperModeAPIs);
-
-BASE_DECLARE_FEATURE(kCheckingNoExtensionIdInExtensionIpcs);
-
-BASE_DECLARE_FEATURE(kNewExtensionFaviconHandling);
-
-BASE_DECLARE_FEATURE(kExtensionDynamicURLRedirection);
-
-BASE_DECLARE_FEATURE(kExtensionsMenuAccessControl);
+BASE_DECLARE_FEATURE(kAllowWithholdingExtensionPermissionsOnInstall);
 
 BASE_DECLARE_FEATURE(kAvoidEarlyExtensionScriptContextCreation);
 
-BASE_DECLARE_FEATURE(kExtensionsOffscreenDocuments);
+BASE_DECLARE_FEATURE(kCheckingNoExtensionIdInExtensionIpcs);
 
-BASE_DECLARE_FEATURE(kNewWebstoreDomain);
+BASE_DECLARE_FEATURE(kContentScriptsMatchOriginAsFallback);
+
+BASE_DECLARE_FEATURE(kExtensionDynamicURLRedirection);
 
 BASE_DECLARE_FEATURE(kExtensionSidePanelIntegration);
 
-BASE_DECLARE_FEATURE(kExtensionWebFileHandlers);
-
 BASE_DECLARE_FEATURE(kExtensionSourceUrlEnforcement);
 
-BASE_DECLARE_FEATURE(kWebviewTagMPArchBehavior);
+BASE_DECLARE_FEATURE(kExtensionWebFileHandlers);
 
 BASE_DECLARE_FEATURE(kExtensionsManifestV3Only);
 
+BASE_DECLARE_FEATURE(kExtensionsMenuAccessControl);
+
+BASE_DECLARE_FEATURE(kExtensionsMenuAccessControlWithPermittedSites);
+
+BASE_DECLARE_FEATURE(kExtensionsOffscreenDocuments);
+
+BASE_DECLARE_FEATURE(kForceWebRequestProxyForTest);
+
 BASE_DECLARE_FEATURE(kMinimumMV3CSPWithInlineSpeculationRules);
 
+BASE_DECLARE_FEATURE(kNewExtensionFaviconHandling);
+
+BASE_DECLARE_FEATURE(kNewWebstoreDomain);
+
+BASE_DECLARE_FEATURE(kReportKeepaliveUkm);
+
+BASE_DECLARE_FEATURE(kRestrictDeveloperModeAPIs);
+
+BASE_DECLARE_FEATURE(kSafeBrowsingCrxAllowlistAutoDisable);
+
+BASE_DECLARE_FEATURE(kSafeBrowsingCrxAllowlistShowWarnings);
+
+BASE_DECLARE_FEATURE(kStructuredCloningForMV3Messaging);
+
 BASE_DECLARE_FEATURE(kTelemetryExtensionPendingApprovalApi);
 
-BASE_DECLARE_FEATURE(kExtensionsMenuAccessControlWithPermittedSites);
+BASE_DECLARE_FEATURE(kWebviewTagMPArchBehavior);
+
+///////////////////////////////////////////////////////////////////////////////
+// STOP!
+// Please don't just add your new feature down here.
+// See the guidance at the top of this file.
+///////////////////////////////////////////////////////////////////////////////
 
 }  // namespace extensions_features
 
diff --git a/extensions/common/features/feature_flags.cc b/extensions/common/features/feature_flags.cc
index f1eab55e..62721e8 100644
--- a/extensions/common/features/feature_flags.cc
+++ b/extensions/common/features/feature_flags.cc
@@ -20,6 +20,7 @@
 // kill switches for extension features. Note any such feature flags must
 // generally be removed once the API has been stable for a few releases.
 const base::Feature* kFeatureFlags[] = {
+    &extensions_features::kApiRuntimeGetContexts,
     &extensions_features::kExtensionsOffscreenDocuments,
     &extensions_features::kNewWebstoreDomain,
     &extensions_features::kTelemetryExtensionPendingApprovalApi,
diff --git a/fuchsia_web/webengine/BUILD.gn b/fuchsia_web/webengine/BUILD.gn
index f794ce2..2926894 100644
--- a/fuchsia_web/webengine/BUILD.gn
+++ b/fuchsia_web/webengine/BUILD.gn
@@ -610,11 +610,10 @@
     "//testing/gtest",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_hlcpp",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.element:fuchsia.element_hlcpp",
-    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.input.virtualkeyboard:fuchsia.input.virtualkeyboard_cpp",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.input.virtualkeyboard:fuchsia.input.virtualkeyboard_hlcpp",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mediacodec:fuchsia.mediacodec_hlcpp",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mem:fuchsia.mem_hlcpp",
-    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3:fuchsia.ui.input3_cpp",
+    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3:fuchsia.ui.input3_hlcpp",
     "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
     "//ui/gfx",
     "//ui/ozone",
diff --git a/fuchsia_web/webengine/browser/frame_impl_browsertest.cc b/fuchsia_web/webengine/browser/frame_impl_browsertest.cc
index 3fb3885..e3cbd81e 100644
--- a/fuchsia_web/webengine/browser/frame_impl_browsertest.cc
+++ b/fuchsia_web/webengine/browser/frame_impl_browsertest.cc
@@ -79,22 +79,6 @@
                void(content::RenderViewHost* render_view_host));
 };
 
-}  // namespace
-
-// Defines a suite of tests that exercise Frame-level functionality, such as
-// navigation commands and page events.
-class FrameImplTest : public FrameImplTestBase {
- public:
-  FrameImplTest() = default;
-  ~FrameImplTest() override = default;
-
-  FrameImplTest(const FrameImplTest&) = delete;
-  FrameImplTest& operator=(const FrameImplTest&) = delete;
-
-  MOCK_METHOD1(OnServeHttpRequest,
-               void(const net::test_server::HttpRequest& request));
-};
-
 std::string GetDocumentVisibilityState(fuchsia::web::Frame* frame) {
   auto visibility = base::MakeRefCounted<base::RefCountedData<std::string>>();
   base::RunLoop loop;
@@ -119,6 +103,22 @@
   return dup;
 }
 
+}  // namespace
+
+// Defines a suite of tests that exercise Frame-level functionality, such as
+// navigation commands and page events.
+class FrameImplTest : public FrameImplTestBase {
+ public:
+  FrameImplTest() = default;
+  ~FrameImplTest() override = default;
+
+  FrameImplTest(const FrameImplTest&) = delete;
+  FrameImplTest& operator=(const FrameImplTest&) = delete;
+
+  MOCK_METHOD1(OnServeHttpRequest,
+               void(const net::test_server::HttpRequest& request));
+};
+
 // Verifies that Frames are initially "hidden", changes to "visible" once the
 // View is attached to a Presenter and back to "hidden" when the View is
 // detached from the Presenter.
@@ -191,16 +191,6 @@
   frame.navigation_listener().RunUntilTitleEquals("hidden");
 }
 
-void VerifyCanGoBackAndForward(FrameForTest& frame,
-                               bool can_go_back_expected,
-                               bool can_go_forward_expected) {
-  auto* state = frame.navigation_listener().current_state();
-  EXPECT_TRUE(state->has_can_go_back());
-  EXPECT_EQ(state->can_go_back(), can_go_back_expected);
-  EXPECT_TRUE(state->has_can_go_forward());
-  EXPECT_EQ(state->can_go_forward(), can_go_forward_expected);
-}
-
 // Verifies that the browser will navigate and generate a navigation listener
 // event when LoadUrl() is called.
 IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigateFrame) {
@@ -364,6 +354,20 @@
   frame.navigation_listener().RunUntilUrlAndTitleEquals(title3, kPage3Title);
 }
 
+namespace {
+
+void VerifyCanGoBackAndForward(FrameForTest& frame,
+                               bool can_go_back_expected,
+                               bool can_go_forward_expected) {
+  auto* state = frame.navigation_listener().current_state();
+  EXPECT_TRUE(state->has_can_go_back());
+  EXPECT_EQ(state->can_go_back(), can_go_back_expected);
+  EXPECT_TRUE(state->has_can_go_forward());
+  EXPECT_EQ(state->can_go_forward(), can_go_forward_expected);
+}
+
+}  // namespace
+
 IN_PROC_BROWSER_TEST_F(FrameImplTest, GoBackAndForward) {
   auto frame = FrameForTest::Create(context(), {});
 
@@ -413,6 +417,8 @@
   VerifyCanGoBackAndForward(frame, true, false);
 }
 
+namespace {
+
 // An HTTP response stream whose response payload can be sent as "chunks"
 // with indeterminate-length pauses in between.
 class ChunkedHttpTransaction {
@@ -529,6 +535,8 @@
   base::OnceClosure on_response_created_;
 };
 
+}  // namespace
+
 IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigationEventDuringPendingLoad) {
   auto frame = FrameForTest::Create(context(), {});
 
@@ -797,6 +805,8 @@
   frame.navigation_listener().RunUntilUrlAndTitleEquals(title1, kPage1Title);
 }
 
+namespace {
+
 // Observes events specific to the Stop() test case.
 struct WebContentsObserverForStop : public content::WebContentsObserver {
   using content::WebContentsObserver::Observe;
@@ -804,6 +814,8 @@
   MOCK_METHOD0(NavigationStopped, void());
 };
 
+}  // namespace
+
 IN_PROC_BROWSER_TEST_F(FrameImplTest, Stop) {
   auto frame = FrameForTest::Create(context(), {});
   base::RunLoop().RunUntilIdle();
@@ -1256,6 +1268,8 @@
   loop.Run();
 }
 
+namespace {
+
 // Helper class for `Frame.Close()` tests, that navigates the `Frame` to an
 // event-recording page, and connects to accumulate the list of events it
 // receives.
@@ -1315,6 +1329,10 @@
 
   const std::vector<std::string>& events() const { return events_; }
 
+  std::string EventsString() const {
+    return "[" + base::JoinString(events_, ", ") + "]";
+  }
+
  private:
   void OnMessage(fuchsia::web::WebMessage message) {
     events_.push_back(std::move(*base::StringFromMemBuffer(message.data())));
@@ -1328,6 +1346,12 @@
   base::test::TestFuture<zx_status_t> epitaph_;
 };
 
+constexpr char kBeforeUnloadEventName[] = "window.beforeunload";
+constexpr char kUnloadEventName[] = "window.unload";
+constexpr char kPageHideEventName[] = "window.pagehide";
+
+}  // namespace
+
 // Verifies that `Close()`ing a `Frame` without an explicit timeout allows
 // graceful teardown, including firing the expected set of events
 // ("beforeunload", "pagehide" and "onunload").
@@ -1346,10 +1370,10 @@
   frame.RunUntilMessagePortClosed();
 
   // Verify that the expected events were delivered!
-  ASSERT_EQ(frame.events().size(), 3u);
-  EXPECT_EQ(frame.events()[0], "window.beforeunload");
-  EXPECT_EQ(frame.events()[1], "window.pagehide");
-  EXPECT_EQ(frame.events()[2], "window.unload");
+  ASSERT_EQ(frame.events().size(), 3u) << frame.EventsString();
+  EXPECT_EQ(frame.events()[0], kBeforeUnloadEventName);
+  EXPECT_EQ(frame.events()[1], kPageHideEventName);
+  EXPECT_EQ(frame.events()[2], kUnloadEventName);
 
   EXPECT_EQ(frame.epitaph().Get(), ZX_OK);
 }
@@ -1374,10 +1398,10 @@
   frame.RunUntilMessagePortClosed();
 
   // Verify that the expected events were delivered!
-  ASSERT_EQ(frame.events().size(), 3u);
-  EXPECT_EQ(frame.events()[0], "window.beforeunload");
-  EXPECT_EQ(frame.events()[1], "window.pagehide");
-  EXPECT_EQ(frame.events()[2], "window.unload");
+  ASSERT_EQ(frame.events().size(), 3u) << frame.EventsString();
+  EXPECT_EQ(frame.events()[0], kBeforeUnloadEventName);
+  EXPECT_EQ(frame.events()[1], kPageHideEventName);
+  EXPECT_EQ(frame.events()[2], kUnloadEventName);
 
   EXPECT_EQ(frame.epitaph().Get(), ZX_OK);
 }
@@ -1396,7 +1420,7 @@
   // Request to gracefully close the Frame, by specifying a non-zero timeout.
   // This can be arbitrarily short, since we are deliberately provoking timeout.
   frame->Close(std::move(fuchsia::web::FrameCloseRequest().set_timeout(
-      base::Milliseconds(1u).ToZxDuration())));
+      base::Microseconds(1u).ToZxDuration())));
 
   // Don't wait for the MessagePort to close, since that doesn't happen in
   // ASAN builds, for some reason (crbug.com/1400304).
@@ -1421,8 +1445,10 @@
 
   frame.RunUntilMessagePortClosed();
 
-  // Verify that no events were observed.
-  EXPECT_EQ(frame.events().size(), 0u);
+  // In practice it is possible for content to have time to receive & process
+  // visibility and unload events, so just check for "beforeunload".
+  EXPECT_THAT(frame.events(), Not(Contains(kBeforeUnloadEventName)))
+      << frame.EventsString();
 
   EXPECT_EQ(frame.epitaph().Get(), ZX_OK);
 }
@@ -1441,6 +1467,8 @@
 
   frame.RunUntilMessagePortClosed();
 
-  // Verify that no events were observed.
-  EXPECT_EQ(frame.events().size(), 0u);
+  // In practice it is possible for content to have time to receive & process
+  // visibility and unload events, so just check for "beforeunload".
+  EXPECT_THAT(frame.events(), Not(Contains(kBeforeUnloadEventName)))
+      << frame.EventsString();
 }
diff --git a/fuchsia_web/webengine/browser/input_browsertest.cc b/fuchsia_web/webengine/browser/input_browsertest.cc
index 14342fe..4a153c48 100644
--- a/fuchsia_web/webengine/browser/input_browsertest.cc
+++ b/fuchsia_web/webengine/browser/input_browsertest.cc
@@ -2,10 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <fidl/fuchsia.input.virtualkeyboard/cpp/wire_messaging.h>
-#include <fidl/fuchsia.ui.input3/cpp/fidl.h>
-#include <lib/async/default.h>
-
+#include <fuchsia/input/virtualkeyboard/cpp/fidl.h>
+#include <fuchsia/ui/input3/cpp/fidl.h>
+#include <fuchsia/ui/input3/cpp/fidl_test_base.h>
 #include <memory>
 
 #include "base/fuchsia/scoped_service_binding.h"
@@ -24,9 +23,11 @@
 #include "fuchsia_web/webengine/test/web_engine_browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using fuchsia_input::Key;
-using fuchsia_ui_input3::KeyEvent;
-using fuchsia_ui_input3::KeyEventType;
+using fuchsia::input::Key;
+using fuchsia::ui::input3::KeyEvent;
+using fuchsia::ui::input3::KeyEventType;
+using fuchsia::ui::input3::KeyMeaning;
+using fuchsia::ui::input3::NonPrintableKey;
 
 namespace {
 
@@ -38,41 +39,42 @@
 // Returns a KeyEvent with |key_meaning| set based on the supplied codepoint,
 // the |key| field left not set.
 KeyEvent CreateCharacterKeyEvent(uint32_t codepoint, KeyEventType event_type) {
-  return {{
-      .timestamp = base::TimeTicks::Now().ToZxTime(),
-      .type = event_type,
-      .key_meaning = fuchsia_ui_input3::KeyMeaning::WithCodepoint(codepoint),
-  }};
+  KeyEvent key_event;
+
+  fuchsia::ui::input3::KeyMeaning meaning;
+  meaning.set_codepoint(codepoint);
+  key_event.set_key_meaning(std::move(meaning));
+  key_event.set_type(event_type);
+  key_event.set_timestamp(base::TimeTicks::Now().ToZxTime());
+  return key_event;
 }
 
 struct KeyEventOptions {
   bool repeat;
-  std::vector<fuchsia_ui_input3::Modifiers> modifiers;
+  std::vector<fuchsia::ui::input3::Modifiers> modifiers;
 };
 
 // Returns a KeyEvent with both |key| and |key_meaning| set.
 KeyEvent CreateKeyEvent(Key key,
-                        fuchsia_ui_input3::KeyMeaning key_meaning,
+                        KeyMeaning key_meaning,
                         KeyEventType event_type,
                         KeyEventOptions options = {}) {
-  KeyEvent key_event{{
-      .timestamp = base::TimeTicks::Now().ToZxTime(),
-      .type = event_type,
-      .key = key,
-      .key_meaning = std::move(key_meaning),
-  }};
-
+  KeyEvent key_event;
+  key_event.set_timestamp(base::TimeTicks::Now().ToZxTime());
+  key_event.set_type(event_type);
+  key_event.set_key(key);
+  key_event.set_key_meaning(std::move(key_meaning));
   if (options.repeat) {
     // Chromium doesn't look at the value of this, it just check if the field is
     // present.
-    key_event.repeat_sequence(1);
+    key_event.set_repeat_sequence(1);
   }
   if (!options.modifiers.empty()) {
-    fuchsia_ui_input3::Modifiers modifiers;
+    fuchsia::ui::input3::Modifiers modifiers;
     for (const auto modifier : options.modifiers) {
       modifiers |= modifier;
     }
-    key_event.modifiers(modifiers);
+    key_event.set_modifiers(modifiers);
   }
   return key_event;
 }
@@ -80,39 +82,37 @@
                         uint32_t codepoint,
                         KeyEventType event_type,
                         KeyEventOptions options = {}) {
-  return CreateKeyEvent(
-      key, fuchsia_ui_input3::KeyMeaning::WithCodepoint(std::move(codepoint)),
-      event_type, options);
+  return CreateKeyEvent(key, KeyMeaning::WithCodepoint(std::move(codepoint)),
+                        event_type, options);
 }
 KeyEvent CreateKeyEvent(Key key,
-                        fuchsia_ui_input3::NonPrintableKey non_printable_key,
+                        NonPrintableKey non_printable_key,
                         KeyEventType event_type,
                         KeyEventOptions options = {}) {
-  return CreateKeyEvent(key,
-                        fuchsia_ui_input3::KeyMeaning::WithNonPrintableKey(
-                            std::move(non_printable_key)),
-                        event_type, options);
+  return CreateKeyEvent(
+      key, KeyMeaning::WithNonPrintableKey(std::move(non_printable_key)),
+      event_type, options);
 }
 
 base::Value::List FuchsiaModifiersToWebModifiers(
-    const std::vector<fuchsia_ui_input3::Modifiers> fuchsia_modifiers) {
+    const std::vector<fuchsia::ui::input3::Modifiers> fuchsia_modifiers) {
   base::Value::List web_modifiers;
   for (const auto modifier : fuchsia_modifiers) {
-    if (modifier == fuchsia_ui_input3::Modifiers::kAlt) {
+    if (modifier == fuchsia::ui::input3::Modifiers::ALT) {
       web_modifiers.Append("Alt");
-    } else if (modifier == fuchsia_ui_input3::Modifiers::kAltGraph) {
+    } else if (modifier == fuchsia::ui::input3::Modifiers::ALT_GRAPH) {
       web_modifiers.Append("AltGraph");
-    } else if (modifier == fuchsia_ui_input3::Modifiers::kCapsLock) {
+    } else if (modifier == fuchsia::ui::input3::Modifiers::CAPS_LOCK) {
       web_modifiers.Append("CapsLock");
-    } else if (modifier == fuchsia_ui_input3::Modifiers::kCtrl) {
+    } else if (modifier == fuchsia::ui::input3::Modifiers::CTRL) {
       web_modifiers.Append("Control");
-    } else if (modifier == fuchsia_ui_input3::Modifiers::kMeta) {
+    } else if (modifier == fuchsia::ui::input3::Modifiers::META) {
       web_modifiers.Append("Meta");
-    } else if (modifier == fuchsia_ui_input3::Modifiers::kNumLock) {
+    } else if (modifier == fuchsia::ui::input3::Modifiers::NUM_LOCK) {
       web_modifiers.Append("NumLock");
-    } else if (modifier == fuchsia_ui_input3::Modifiers::kScrollLock) {
+    } else if (modifier == fuchsia::ui::input3::Modifiers::SCROLL_LOCK) {
       web_modifiers.Append("ScrollLock");
-    } else if (modifier == fuchsia_ui_input3::Modifiers::kShift) {
+    } else if (modifier == fuchsia::ui::input3::Modifiers::SHIFT) {
       web_modifiers.Append("Shift");
     } else {
       NOTREACHED() << static_cast<uint64_t>(modifier) << " has no web mapping";
@@ -134,46 +134,49 @@
   return base::Value(std::move(expected));
 }
 
-class FakeKeyboard : public fidl::Server<fuchsia_ui_input3::Keyboard> {
+class FakeKeyboard : public fuchsia::ui::input3::testing::Keyboard_TestBase {
  public:
   explicit FakeKeyboard(sys::OutgoingDirectory* additional_services)
-      : binding_(additional_services, this, async_get_default_dispatcher()) {}
+      : binding_(additional_services, this) {}
   ~FakeKeyboard() override = default;
 
   FakeKeyboard(const FakeKeyboard&) = delete;
   FakeKeyboard& operator=(const FakeKeyboard&) = delete;
 
-  base::ScopedNaturalServiceBinding<fuchsia_ui_input3::Keyboard>* binding() {
+  base::ScopedServiceBinding<fuchsia::ui::input3::Keyboard>* binding() {
     return &binding_;
   }
 
   // Sends |key_event| to |listener_|;
   void SendKeyEvent(KeyEvent key_event) {
-    listener_->OnKeyEvent(std::move(key_event))
-        .ThenExactlyOnce(
-            [num_sent_events = num_sent_events_,
-             this](const fidl::Result<
-                   fuchsia_ui_input3::KeyboardListener::OnKeyEvent>& result) {
-              ASSERT_EQ(num_acked_events_, num_sent_events)
-                  << "Key events are acked out of order";
-              num_acked_events_++;
-            });
+    listener_->OnKeyEvent(std::move(key_event),
+                          [num_sent_events = num_sent_events_,
+                           this](fuchsia::ui::input3::KeyEventStatus status) {
+                            ASSERT_EQ(num_acked_events_, num_sent_events)
+                                << "Key events are acked out of order";
+                            num_acked_events_++;
+                          });
     num_sent_events_++;
   }
 
-  // fuchsia_ui_input3::Keyboard implementation.
-  void AddListener(AddListenerRequest& request,
-                   AddListenerCompleter::Sync& completer) final {
+  // fuchsia::ui::input3::Keyboard implementation.
+  void AddListener(
+      fuchsia::ui::views::ViewRef view_ref,
+      fidl::InterfaceHandle<::fuchsia::ui::input3::KeyboardListener> listener,
+      AddListenerCallback callback) final {
     // This implementation is only set up to have up to one listener.
     EXPECT_FALSE(listener_);
-    listener_.Bind(std::move(request.listener()),
-                   async_get_default_dispatcher());
-    completer.Reply();
+    listener_ = listener.Bind();
+    callback();
+  }
+
+  void NotImplemented_(const std::string& name) final {
+    NOTIMPLEMENTED() << name;
   }
 
  private:
-  fidl::Client<fuchsia_ui_input3::KeyboardListener> listener_;
-  base::ScopedNaturalServiceBinding<fuchsia_ui_input3::Keyboard> binding_;
+  fuchsia::ui::input3::KeyboardListenerPtr listener_;
+  base::ScopedServiceBinding<fuchsia::ui::input3::Keyboard> binding_;
 
   // Counters to make sure key events are acked in order.
   int num_sent_events_ = 0;
@@ -211,8 +214,7 @@
     component_context_.emplace(
         base::TestComponentContextForProcess::InitialState::kCloneAll);
     component_context_->additional_services()
-        ->RemovePublicService<fuchsia_ui_input3::Keyboard>(
-            fidl::DiscoverableProtocolName<fuchsia_ui_input3::Keyboard>);
+        ->RemovePublicService<fuchsia::ui::input3::Keyboard>();
     SetUpService();
     virtual_keyboard_checker_.emplace(
         component_context_->additional_services());
@@ -267,7 +269,7 @@
   absl::optional<FakeKeyboard> keyboard_service_;
   base::test::ScopedFeatureList scoped_feature_list_;
   absl::optional<
-      NeverConnectedChecker<fuchsia_input_virtualkeyboard::ControllerCreator>>
+      NeverConnectedChecker<fuchsia::input::virtualkeyboard::ControllerCreator>>
       virtual_keyboard_checker_;
 };
 
@@ -277,13 +279,13 @@
   // Pressing character keys will generate a JavaScript keydown event followed
   // by a keypress event. Releasing any key generates a keyup event.
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kA, 'a', KeyEventType::kPressed));
+      CreateKeyEvent(Key::A, 'a', KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kKey8, '8', KeyEventType::kPressed));
+      CreateKeyEvent(Key::KEY_8, '8', KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kKey8, '8', KeyEventType::kReleased));
+      CreateKeyEvent(Key::KEY_8, '8', KeyEventType::RELEASED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kA, 'a', KeyEventType::kReleased));
+      CreateKeyEvent(Key::A, 'a', KeyEventType::RELEASED));
 
   ExpectKeyEventsEqual(ExpectedKeyValue("KeyA", "a", kKeyDown),
                        ExpectedKeyValue("KeyA", "a", kKeyPress),
@@ -299,11 +301,11 @@
   // Pressing character keys will generate a JavaScript keydown event followed
   // by a keypress event. Releasing any key generates a keyup event.
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent('A', KeyEventType::kPressed));
+      CreateCharacterKeyEvent('A', KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent('A', KeyEventType::kReleased));
+      CreateCharacterKeyEvent('A', KeyEventType::RELEASED));
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent('b', KeyEventType::kPressed));
+      CreateCharacterKeyEvent('b', KeyEventType::PRESSED));
 
   ExpectKeyEventsEqual(
       ExpectedKeyValue("", "A", kKeyDown), ExpectedKeyValue("", "A", kKeyPress),
@@ -316,13 +318,13 @@
   // TODO(fxbug.dev/106600): Update the WithCodepoint(0)s when the platform is
   // fixed to provide valid KeyMeanings for these keys.
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kLeftShift, 0, KeyEventType::kPressed));
+      CreateKeyEvent(Key::LEFT_SHIFT, 0, KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent('a', KeyEventType::kPressed));
+      CreateCharacterKeyEvent('a', KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent('a', KeyEventType::kReleased));
+      CreateCharacterKeyEvent('a', KeyEventType::RELEASED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kLeftShift, 0, KeyEventType::kReleased));
+      CreateKeyEvent(Key::LEFT_SHIFT, 0, KeyEventType::RELEASED));
 
   ExpectKeyEventsEqual(
       ExpectedKeyValue("ShiftLeft", "Shift", kKeyDown),
@@ -336,9 +338,9 @@
 IN_PROC_BROWSER_TEST_F(KeyboardInputTest, CharacterInBmp) {
   const wchar_t kSigma = 0x03C3;
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent(kSigma, KeyEventType::kPressed));
+      CreateCharacterKeyEvent(kSigma, KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent(kSigma, KeyEventType::kReleased));
+      CreateCharacterKeyEvent(kSigma, KeyEventType::RELEASED));
 
   std::string expected_utf8;
   ASSERT_TRUE(base::WideToUTF8(&kSigma, 1, &expected_utf8));
@@ -353,13 +355,13 @@
   const uint32_t kRamenEmoji = 0x1F35C;
 
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent(kRamenEmoji, KeyEventType::kPressed));
+      CreateCharacterKeyEvent(kRamenEmoji, KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent(kRamenEmoji, KeyEventType::kReleased));
+      CreateCharacterKeyEvent(kRamenEmoji, KeyEventType::RELEASED));
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent('a', KeyEventType::kPressed));
+      CreateCharacterKeyEvent('a', KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateCharacterKeyEvent('a', KeyEventType::kReleased));
+      CreateCharacterKeyEvent('a', KeyEventType::RELEASED));
 
   ExpectKeyEventsEqual(ExpectedKeyValue("", "a", kKeyDown),
                        ExpectedKeyValue("", "a", kKeyPress),
@@ -368,17 +370,17 @@
 
 IN_PROC_BROWSER_TEST_F(KeyboardInputTest, ShiftPrintableKeys) {
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kLeftShift, 0, KeyEventType::kPressed));
+      CreateKeyEvent(Key::LEFT_SHIFT, 0, KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kB, 'B', KeyEventType::kPressed));
+      CreateKeyEvent(Key::B, 'B', KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kKey1, '!', KeyEventType::kPressed));
+      CreateKeyEvent(Key::KEY_1, '!', KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kSpace, ' ', KeyEventType::kPressed));
+      CreateKeyEvent(Key::SPACE, ' ', KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kLeftShift, 0, KeyEventType::kReleased));
+      CreateKeyEvent(Key::LEFT_SHIFT, 0, KeyEventType::RELEASED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kDot, '.', KeyEventType::kPressed));
+      CreateKeyEvent(Key::DOT, '.', KeyEventType::PRESSED));
 
   // Note that non-character keys (e.g. shift, control) only generate key down
   // and key up web events. They do not generate key pressed events.
@@ -396,14 +398,13 @@
 
 IN_PROC_BROWSER_TEST_F(KeyboardInputTest, ShiftNonPrintableKeys) {
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kRightShift, 0, KeyEventType::kPressed));
+      CreateKeyEvent(Key::RIGHT_SHIFT, 0, KeyEventType::PRESSED));
+  keyboard_service_->SendKeyEvent(CreateKeyEvent(
+      Key::ENTER, NonPrintableKey::ENTER, KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kEnter, fuchsia_ui_input3::NonPrintableKey::kEnter,
-                     KeyEventType::kPressed));
+      CreateKeyEvent(Key::LEFT_CTRL, 0, KeyEventType::PRESSED));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kLeftCtrl, 0, KeyEventType::kPressed));
-  keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kRightShift, 0, KeyEventType::kReleased));
+      CreateKeyEvent(Key::RIGHT_SHIFT, 0, KeyEventType::RELEASED));
 
   // Note that non-character keys (e.g. shift, control) only generate key down
   // and key up web events. They do not generate key pressed events.
@@ -416,9 +417,9 @@
 
 IN_PROC_BROWSER_TEST_F(KeyboardInputTest, RepeatedKeys) {
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kA, 'a', KeyEventType::kPressed, {.repeat = true}));
-  keyboard_service_->SendKeyEvent(CreateKeyEvent(
-      Key::kKey8, '8', KeyEventType::kPressed, {.repeat = true}));
+      CreateKeyEvent(Key::A, 'a', KeyEventType::PRESSED, {.repeat = true}));
+  keyboard_service_->SendKeyEvent(
+      CreateKeyEvent(Key::KEY_8, '8', KeyEventType::PRESSED, {.repeat = true}));
 
   // Note that non-character keys (e.g. shift, control) only generate key down
   // and key up web events. They do not generate key pressed events.
@@ -436,17 +437,17 @@
   // * LEFT_*/RIGHT_* are not valid by themselves.
   // * FUNCTION and SYMBOL. See AllUnsupportedWebModifierKeys test.
   const std::vector kAllSupportedModifiers = {
-      fuchsia_ui_input3::Modifiers::kCapsLock,
-      fuchsia_ui_input3::Modifiers::kNumLock,
-      fuchsia_ui_input3::Modifiers::kScrollLock,
-      fuchsia_ui_input3::Modifiers::kShift,
-      fuchsia_ui_input3::Modifiers::kAlt,
-      fuchsia_ui_input3::Modifiers::kAltGraph,
-      fuchsia_ui_input3::Modifiers::kMeta,
-      fuchsia_ui_input3::Modifiers::kCtrl};
+      fuchsia::ui::input3::Modifiers::CAPS_LOCK,
+      fuchsia::ui::input3::Modifiers::NUM_LOCK,
+      fuchsia::ui::input3::Modifiers::SCROLL_LOCK,
+      fuchsia::ui::input3::Modifiers::SHIFT,
+      fuchsia::ui::input3::Modifiers::ALT,
+      fuchsia::ui::input3::Modifiers::ALT_GRAPH,
+      fuchsia::ui::input3::Modifiers::META,
+      fuchsia::ui::input3::Modifiers::CTRL};
   for (const auto& modifier : kAllSupportedModifiers) {
     keyboard_service_->SendKeyEvent(CreateKeyEvent(
-        Key::kM, 'm', KeyEventType::kPressed, {.modifiers = {modifier}}));
+        Key::M, 'm', KeyEventType::PRESSED, {.modifiers = {modifier}}));
   }
 
   base::Value::List expected_events;
@@ -455,8 +456,8 @@
         ExpectedKeyValue("KeyM", "m", kKeyDown, {.modifiers = {modifier}}));
     // Chrome doesn't emit keypress events when an ALT or CTRL modifier is
     // present.
-    if (modifier != fuchsia_ui_input3::Modifiers::kAlt &&
-        modifier != fuchsia_ui_input3::Modifiers::kCtrl) {
+    if (modifier != fuchsia::ui::input3::Modifiers::ALT &&
+        modifier != fuchsia::ui::input3::Modifiers::CTRL) {
       expected_events.Append(
           ExpectedKeyValue("KeyM", "m", kKeyPress, {.modifiers = {modifier}}));
     }
@@ -470,11 +471,11 @@
   // because they aren't included in
   // https://crsrc.org/c/ui/events/blink/blink_event_util.cc;l=268?q=EventFlagsToWebEventModifiers
   const std::vector kAllUnsupportedModifiers = {
-      fuchsia_ui_input3::Modifiers::kFunction,
-      fuchsia_ui_input3::Modifiers::kSymbol};
+      fuchsia::ui::input3::Modifiers::FUNCTION,
+      fuchsia::ui::input3::Modifiers::SYMBOL};
   for (const auto& modifier : kAllUnsupportedModifiers) {
     keyboard_service_->SendKeyEvent(CreateKeyEvent(
-        Key::kM, 'm', KeyEventType::kPressed, {.modifiers = {modifier}}));
+        Key::M, 'm', KeyEventType::PRESSED, {.modifiers = {modifier}}));
   }
 
   base::Value::List expected_events;
@@ -491,75 +492,76 @@
 IN_PROC_BROWSER_TEST_F(KeyboardInputTest, AssortedModifierKeyCombos) {
   // Test that sending LEFT/RIGHT SHIFT with agnostic SHIFT passes DCHECK.
   keyboard_service_->SendKeyEvent(CreateKeyEvent(
-      Key::kA, 'a', KeyEventType::kPressed,
-      {.modifiers = {fuchsia_ui_input3::Modifiers::kShift,
-                     fuchsia_ui_input3::Modifiers::kLeftShift,
-                     fuchsia_ui_input3::Modifiers::kRightShift}}));
+      Key::A, 'a', KeyEventType::PRESSED,
+      {.modifiers = {fuchsia::ui::input3::Modifiers::SHIFT,
+                     fuchsia::ui::input3::Modifiers::LEFT_SHIFT,
+                     fuchsia::ui::input3::Modifiers::RIGHT_SHIFT}}));
   // Test that sending LEFT/RIGHT ALT with agnostic ALT passes DCHECK.
-  keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kKey8, '8', KeyEventType::kPressed,
-                     {.modifiers = {fuchsia_ui_input3::Modifiers::kAlt,
-                                    fuchsia_ui_input3::Modifiers::kLeftAlt,
-                                    fuchsia_ui_input3::Modifiers::kRightAlt}}));
+  keyboard_service_->SendKeyEvent(CreateKeyEvent(
+      Key::KEY_8, '8', KeyEventType::PRESSED,
+      {.modifiers = {fuchsia::ui::input3::Modifiers::ALT,
+                     fuchsia::ui::input3::Modifiers::LEFT_ALT,
+                     fuchsia::ui::input3::Modifiers::RIGHT_ALT}}));
   // Test that sending LEFT/RIGHT META with agnostic META passes DCHECK.
   keyboard_service_->SendKeyEvent(CreateKeyEvent(
-      Key::kB, 'b', KeyEventType::kPressed,
-      {.modifiers = {fuchsia_ui_input3::Modifiers::kMeta,
-                     fuchsia_ui_input3::Modifiers::kLeftMeta,
-                     fuchsia_ui_input3::Modifiers::kRightMeta}}));
+      Key::B, 'b', KeyEventType::PRESSED,
+      {.modifiers = {fuchsia::ui::input3::Modifiers::META,
+                     fuchsia::ui::input3::Modifiers::LEFT_META,
+                     fuchsia::ui::input3::Modifiers::RIGHT_META}}));
   // Test that sending LEFT/RIGHT CTRL with agnostic CTRL passes DCHECK.
   keyboard_service_->SendKeyEvent(CreateKeyEvent(
-      Key::kLeft, 0, KeyEventType::kPressed,
-      {.modifiers = {fuchsia_ui_input3::Modifiers::kCtrl,
-                     fuchsia_ui_input3::Modifiers::kLeftCtrl,
-                     fuchsia_ui_input3::Modifiers::kRightCtrl}}));
+      Key::LEFT, 0, KeyEventType::PRESSED,
+      {.modifiers = {fuchsia::ui::input3::Modifiers::CTRL,
+                     fuchsia::ui::input3::Modifiers::LEFT_CTRL,
+                     fuchsia::ui::input3::Modifiers::RIGHT_CTRL}}));
   keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kP, 'p', KeyEventType::kPressed,
-                     {.modifiers = {fuchsia_ui_input3::Modifiers::kCtrl,
-                                    fuchsia_ui_input3::Modifiers::kShift}}));
-  keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kRight, 0, KeyEventType::kPressed,
-                     {.modifiers = {fuchsia_ui_input3::Modifiers::kAltGraph}}));
-  keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kUp, 0, KeyEventType::kPressed,
-                     {.modifiers = {fuchsia_ui_input3::Modifiers::kCapsLock}}));
-  keyboard_service_->SendKeyEvent(
-      CreateKeyEvent(Key::kDown, 0, KeyEventType::kPressed,
-                     {.modifiers = {fuchsia_ui_input3::Modifiers::kNumLock}}));
+      CreateKeyEvent(Key::P, 'p', KeyEventType::PRESSED,
+                     {.modifiers = {fuchsia::ui::input3::Modifiers::CTRL,
+                                    fuchsia::ui::input3::Modifiers::SHIFT}}));
   keyboard_service_->SendKeyEvent(CreateKeyEvent(
-      Key::kLeft, 0, KeyEventType::kPressed,
-      {.modifiers = {fuchsia_ui_input3::Modifiers::kScrollLock}}));
+      Key::RIGHT, 0, KeyEventType::PRESSED,
+      {.modifiers = {fuchsia::ui::input3::Modifiers::ALT_GRAPH}}));
+  keyboard_service_->SendKeyEvent(CreateKeyEvent(
+      Key::UP, 0, KeyEventType::PRESSED,
+      {.modifiers = {fuchsia::ui::input3::Modifiers::CAPS_LOCK}}));
+  keyboard_service_->SendKeyEvent(CreateKeyEvent(
+      Key::DOWN, 0, KeyEventType::PRESSED,
+      {.modifiers = {fuchsia::ui::input3::Modifiers::NUM_LOCK}}));
+  keyboard_service_->SendKeyEvent(CreateKeyEvent(
+      Key::LEFT, 0, KeyEventType::PRESSED,
+      {.modifiers = {fuchsia::ui::input3::Modifiers::SCROLL_LOCK}}));
 
   ExpectKeyEventsEqual(
       ExpectedKeyValue("KeyA", "a", kKeyDown,
-                       {.modifiers = {fuchsia_ui_input3::Modifiers::kShift}}),
+                       {.modifiers = {fuchsia::ui::input3::Modifiers::SHIFT}}),
       ExpectedKeyValue("KeyA", "a", kKeyPress,
-                       {.modifiers = {fuchsia_ui_input3::Modifiers::kShift}}),
+                       {.modifiers = {fuchsia::ui::input3::Modifiers::SHIFT}}),
       ExpectedKeyValue("Digit8", "8", kKeyDown,
-                       {.modifiers = {fuchsia_ui_input3::Modifiers::kAlt}}),
+                       {.modifiers = {fuchsia::ui::input3::Modifiers::ALT}}),
       ExpectedKeyValue("KeyB", "b", kKeyDown,
-                       {.modifiers = {fuchsia_ui_input3::Modifiers::kMeta}}),
+                       {.modifiers = {fuchsia::ui::input3::Modifiers::META}}),
       ExpectedKeyValue("KeyB", "b", kKeyPress,
-                       {.modifiers = {fuchsia_ui_input3::Modifiers::kMeta}}),
+                       {.modifiers = {fuchsia::ui::input3::Modifiers::META}}),
       ExpectedKeyValue("ArrowLeft", "ArrowLeft", kKeyDown,
-                       {.modifiers = {fuchsia_ui_input3::Modifiers::kCtrl}}),
+                       {.modifiers = {fuchsia::ui::input3::Modifiers::CTRL}}),
       ExpectedKeyValue("KeyP", "p", kKeyDown,
-                       {.modifiers = {fuchsia_ui_input3::Modifiers::kCtrl,
-                                      fuchsia_ui_input3::Modifiers::kShift}}),
+                       {.modifiers = {fuchsia::ui::input3::Modifiers::CTRL,
+                                      fuchsia::ui::input3::Modifiers::SHIFT}}),
       ExpectedKeyValue("KeyP", "p", kKeyPress,
-                       {.modifiers = {fuchsia_ui_input3::Modifiers::kCtrl,
-                                      fuchsia_ui_input3::Modifiers::kShift}}),
+                       {.modifiers = {fuchsia::ui::input3::Modifiers::CTRL,
+                                      fuchsia::ui::input3::Modifiers::SHIFT}}),
       ExpectedKeyValue(
           "ArrowRight", "ArrowRight", kKeyDown,
-          {.modifiers = {fuchsia_ui_input3::Modifiers::kAltGraph}}),
+          {.modifiers = {fuchsia::ui::input3::Modifiers::ALT_GRAPH}}),
       ExpectedKeyValue(
           "ArrowUp", "ArrowUp", kKeyDown,
-          {.modifiers = {fuchsia_ui_input3::Modifiers::kCapsLock}}),
-      ExpectedKeyValue("ArrowDown", "ArrowDown", kKeyDown,
-                       {.modifiers = {fuchsia_ui_input3::Modifiers::kNumLock}}),
+          {.modifiers = {fuchsia::ui::input3::Modifiers::CAPS_LOCK}}),
+      ExpectedKeyValue(
+          "ArrowDown", "ArrowDown", kKeyDown,
+          {.modifiers = {fuchsia::ui::input3::Modifiers::NUM_LOCK}}),
       ExpectedKeyValue(
           "ArrowLeft", "ArrowLeft", kKeyDown,
-          {.modifiers = {fuchsia_ui_input3::Modifiers::kScrollLock}}));
+          {.modifiers = {fuchsia::ui::input3::Modifiers::SCROLL_LOCK}}));
 }
 
 IN_PROC_BROWSER_TEST_F(KeyboardInputTest, Disconnect) {
@@ -588,7 +590,7 @@
     keyboard_input_checker_.emplace(component_context_->additional_services());
   }
 
-  absl::optional<NeverConnectedChecker<fuchsia_ui_input3::Keyboard>>
+  absl::optional<NeverConnectedChecker<fuchsia::ui::input3::Keyboard>>
       keyboard_input_checker_;
 };
 
diff --git a/fuchsia_web/webengine/browser/virtual_keyboard_browsertest.cc b/fuchsia_web/webengine/browser/virtual_keyboard_browsertest.cc
index 7b239cf..6203645 100644
--- a/fuchsia_web/webengine/browser/virtual_keyboard_browsertest.cc
+++ b/fuchsia_web/webengine/browser/virtual_keyboard_browsertest.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <fidl/fuchsia.ui.input3/cpp/fidl.h>
 #include <fuchsia/input/virtualkeyboard/cpp/fidl.h>
+#include <fuchsia/ui/input3/cpp/fidl.h>
 #include <lib/fit/function.h>
 
 #include "base/fuchsia/fuchsia_logging.h"
@@ -73,8 +73,7 @@
 
     // Ensure that the fuchsia.ui.input3.Keyboard service is connected.
     component_context_->additional_services()
-        ->RemovePublicService<fuchsia_ui_input3::Keyboard>(
-            fidl::DiscoverableProtocolName<fuchsia_ui_input3::Keyboard>);
+        ->RemovePublicService<fuchsia::ui::input3::Keyboard>();
     keyboard_input_checker_.emplace(component_context_->additional_services());
 
     fuchsia::web::NavigationControllerPtr controller;
@@ -135,7 +134,7 @@
   ScenicTestHelper scenic_test_helper_;
   base::test::ScopedFeatureList scoped_feature_list_;
 
-  absl::optional<EnsureConnectedChecker<fuchsia_ui_input3::Keyboard>>
+  absl::optional<EnsureConnectedChecker<fuchsia::ui::input3::Keyboard>>
       keyboard_input_checker_;
 
   // Fake virtual keyboard services for the InputMethod to use.
diff --git a/fuchsia_web/webengine/test/scoped_connection_checker.h b/fuchsia_web/webengine/test/scoped_connection_checker.h
index 9a6b313..8bc2441 100644
--- a/fuchsia_web/webengine/test/scoped_connection_checker.h
+++ b/fuchsia_web/webengine/test/scoped_connection_checker.h
@@ -6,9 +6,7 @@
 #define FUCHSIA_WEB_WEBENGINE_TEST_SCOPED_CONNECTION_CHECKER_H_
 
 #include <lib/fdio/directory.h>
-#include <lib/fidl/cpp/wire/connect_service.h>
 #include <lib/vfs/cpp/service.h>
-
 #include <memory>
 #include <string>
 
@@ -16,8 +14,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 // Verifies that a connection was made, or never attempted, for a given
-// `Protocol` without needing to provide an implementation.
-template <typename Protocol, bool expect_connection>
+// |Service| without needing to provide an implementation.
+template <typename Service, bool expect_connection>
 class ScopedConnectionCheckerBase {
  public:
   explicit ScopedConnectionCheckerBase(
@@ -27,7 +25,7 @@
             [this](zx::channel channel, async_dispatcher_t*) {
               pending_channels_.push_back(std::move(channel));
             }),
-        fidl::DiscoverableProtocolName<Protocol>);
+        std::string(Service::Name_));
 
     ZX_CHECK(status == ZX_OK, status) << "OutgoingDirectory::AddPublicService";
   }
@@ -45,16 +43,16 @@
 
  private:
   // Client channels are held in a pending (unconnected) state for the
-  // lifetime of `this`, so that the client never sees a disconnection event.
+  // lifetime of |this|, so that the client never sees a disconnection event.
   std::vector<zx::channel> pending_channels_;
 };
 
-// Checks that no client attempted to connect to `Protocol`.
-template <typename Protocol>
-using NeverConnectedChecker = ScopedConnectionCheckerBase<Protocol, false>;
+// Checks that no client attempted to connect to |Service|.
+template <typename Service>
+using NeverConnectedChecker = ScopedConnectionCheckerBase<Service, false>;
 
-// Checks that at least one client attempted to connect to `Protocol`.
-template <typename Protocol>
-using EnsureConnectedChecker = ScopedConnectionCheckerBase<Protocol, true>;
+// Checks that at least one client attempted to connect to |Service|.
+template <typename Service>
+using EnsureConnectedChecker = ScopedConnectionCheckerBase<Service, true>;
 
 #endif  // FUCHSIA_WEB_WEBENGINE_TEST_SCOPED_CONNECTION_CHECKER_H_
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h
index fcf5beb4..8b5e858 100644
--- a/gpu/GLES2/gl2chromium_autogen.h
+++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -402,6 +402,10 @@
 #define glEndPixelLocalStorageANGLE GLES2_GET_FUN(EndPixelLocalStorageANGLE)
 #define glPixelLocalStorageBarrierANGLE \
   GLES2_GET_FUN(PixelLocalStorageBarrierANGLE)
+#define glFramebufferPixelLocalStorageInterruptANGLE \
+  GLES2_GET_FUN(FramebufferPixelLocalStorageInterruptANGLE)
+#define glFramebufferPixelLocalStorageRestoreANGLE \
+  GLES2_GET_FUN(FramebufferPixelLocalStorageRestoreANGLE)
 #define glGetFramebufferPixelLocalStorageParameterfvANGLE \
   GLES2_GET_FUN(GetFramebufferPixelLocalStorageParameterfvANGLE)
 #define glGetFramebufferPixelLocalStorageParameterivANGLE \
diff --git a/gpu/GLES2/gl2extchromium.h b/gpu/GLES2/gl2extchromium.h
index 4a5000b0..7d4909c 100644
--- a/gpu/GLES2/gl2extchromium.h
+++ b/gpu/GLES2/gl2extchromium.h
@@ -385,6 +385,8 @@
 GL_APICALL void GL_APIENTRY
 glEndPixelLocalStorageANGLE(GLsizei n, const GLenum storeops[]);
 GL_APICALL void GL_APIENTRY glPixelLocalStorageBarrierANGLE(void);
+GL_APICALL void GL_APIENTRY glFramebufferPixelLocalStorageInterruptANGLE(void);
+GL_APICALL void GL_APIENTRY glFramebufferPixelLocalStorageRestoreANGLE(void);
 GL_APICALL void GL_APIENTRY
 glGetFramebufferPixelLocalStorageParameterfvANGLE(GLint plane,
                                                   GLenum pname,
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index f533946..5121b34 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -4164,6 +4164,20 @@
     'es3': True,
     'decoder_func': 'DoPixelLocalStorageBarrierANGLE',
   },
+  'FramebufferPixelLocalStorageInterruptANGLE': {
+    'extension': 'ANGLE_shader_pixel_local_storage',
+    'extension_flag': 'angle_shader_pixel_local_storage',
+    'unit_test': False,
+    'es3': True,
+    'decoder_func': 'DoFramebufferPixelLocalStorageInterruptANGLE',
+  },
+  'FramebufferPixelLocalStorageRestoreANGLE': {
+    'extension': 'ANGLE_shader_pixel_local_storage',
+    'extension_flag': 'angle_shader_pixel_local_storage',
+    'unit_test': False,
+    'es3': True,
+    'decoder_func': 'DoFramebufferPixelLocalStorageRestoreANGLE',
+  },
   'GetFramebufferPixelLocalStorageParameterfvANGLE': {
     'extension': 'ANGLE_shader_pixel_local_storage',
     'extension_flag': 'angle_shader_pixel_local_storage',
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index 38b2d92..67d4beb9 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -1790,6 +1790,12 @@
 void GL_APIENTRY GLES2PixelLocalStorageBarrierANGLE() {
   gles2::GetGLContext()->PixelLocalStorageBarrierANGLE();
 }
+void GL_APIENTRY GLES2FramebufferPixelLocalStorageInterruptANGLE() {
+  gles2::GetGLContext()->FramebufferPixelLocalStorageInterruptANGLE();
+}
+void GL_APIENTRY GLES2FramebufferPixelLocalStorageRestoreANGLE() {
+  gles2::GetGLContext()->FramebufferPixelLocalStorageRestoreANGLE();
+}
 void GL_APIENTRY
 GLES2GetFramebufferPixelLocalStorageParameterfvANGLE(GLint plane,
                                                      GLenum pname,
@@ -3242,6 +3248,16 @@
         reinterpret_cast<GLES2FunctionPointer>(glPixelLocalStorageBarrierANGLE),
     },
     {
+        "glFramebufferPixelLocalStorageInterruptANGLE",
+        reinterpret_cast<GLES2FunctionPointer>(
+            glFramebufferPixelLocalStorageInterruptANGLE),
+    },
+    {
+        "glFramebufferPixelLocalStorageRestoreANGLE",
+        reinterpret_cast<GLES2FunctionPointer>(
+            glFramebufferPixelLocalStorageRestoreANGLE),
+    },
+    {
         "glGetFramebufferPixelLocalStorageParameterfvANGLE",
         reinterpret_cast<GLES2FunctionPointer>(
             glGetFramebufferPixelLocalStorageParameterfvANGLE),
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index 6f7ada2b..67f2d90 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -3418,6 +3418,22 @@
   }
 }
 
+void FramebufferPixelLocalStorageInterruptANGLE() {
+  gles2::cmds::FramebufferPixelLocalStorageInterruptANGLE* c =
+      GetCmdSpace<gles2::cmds::FramebufferPixelLocalStorageInterruptANGLE>();
+  if (c) {
+    c->Init();
+  }
+}
+
+void FramebufferPixelLocalStorageRestoreANGLE() {
+  gles2::cmds::FramebufferPixelLocalStorageRestoreANGLE* c =
+      GetCmdSpace<gles2::cmds::FramebufferPixelLocalStorageRestoreANGLE>();
+  if (c) {
+    c->Init();
+  }
+}
+
 void GetFramebufferPixelLocalStorageParameterfvANGLE(
     GLint plane,
     GLenum pname,
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index 3348932..247757d 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -1257,6 +1257,10 @@
 
 void PixelLocalStorageBarrierANGLE() override;
 
+void FramebufferPixelLocalStorageInterruptANGLE() override;
+
+void FramebufferPixelLocalStorageRestoreANGLE() override;
+
 void GetFramebufferPixelLocalStorageParameterfvANGLE(GLint plane,
                                                      GLenum pname,
                                                      GLfloat* params) override;
diff --git a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
index 1174f6f..eb3c94a 100644
--- a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
@@ -3789,6 +3789,24 @@
   CheckGLError();
 }
 
+void GLES2Implementation::FramebufferPixelLocalStorageInterruptANGLE() {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix()
+                     << "] glFramebufferPixelLocalStorageInterruptANGLE("
+                     << ")");
+  helper_->FramebufferPixelLocalStorageInterruptANGLE();
+  CheckGLError();
+}
+
+void GLES2Implementation::FramebufferPixelLocalStorageRestoreANGLE() {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix()
+                     << "] glFramebufferPixelLocalStorageRestoreANGLE("
+                     << ")");
+  helper_->FramebufferPixelLocalStorageRestoreANGLE();
+  CheckGLError();
+}
+
 void GLES2Implementation::GetFramebufferPixelLocalStorageParameterfvANGLE(
     GLint plane,
     GLenum pname,
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
index 44f9d8c..2ac33f0 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
@@ -3275,6 +3275,28 @@
   EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
 }
 
+TEST_F(GLES2ImplementationTest, FramebufferPixelLocalStorageInterruptANGLE) {
+  struct Cmds {
+    cmds::FramebufferPixelLocalStorageInterruptANGLE cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init();
+
+  gl_->FramebufferPixelLocalStorageInterruptANGLE();
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(GLES2ImplementationTest, FramebufferPixelLocalStorageRestoreANGLE) {
+  struct Cmds {
+    cmds::FramebufferPixelLocalStorageRestoreANGLE cmd;
+  };
+  Cmds expected;
+  expected.cmd.Init();
+
+  gl_->FramebufferPixelLocalStorageRestoreANGLE();
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
 TEST_F(GLES2ImplementationTest,
        GetFramebufferPixelLocalStorageParameterfvANGLE) {
   struct Cmds {
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h
index c83502f9..3da4dc1c 100644
--- a/gpu/command_buffer/client/gles2_interface_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -936,6 +936,8 @@
 virtual void EndPixelLocalStorageANGLE(GLsizei count,
                                        const GLenum* storeops) = 0;
 virtual void PixelLocalStorageBarrierANGLE() = 0;
+virtual void FramebufferPixelLocalStorageInterruptANGLE() = 0;
+virtual void FramebufferPixelLocalStorageRestoreANGLE() = 0;
 virtual void GetFramebufferPixelLocalStorageParameterfvANGLE(
     GLint plane,
     GLenum pname,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
index 989697a2..b53e704 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -906,6 +906,8 @@
 void BeginPixelLocalStorageANGLE(GLsizei count, const GLenum* loadops) override;
 void EndPixelLocalStorageANGLE(GLsizei count, const GLenum* storeops) override;
 void PixelLocalStorageBarrierANGLE() override;
+void FramebufferPixelLocalStorageInterruptANGLE() override;
+void FramebufferPixelLocalStorageRestoreANGLE() override;
 void GetFramebufferPixelLocalStorageParameterfvANGLE(GLint plane,
                                                      GLenum pname,
                                                      GLfloat* params) override;
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
index ef6c109..f033915f 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -1223,6 +1223,8 @@
     GLsizei /* count */,
     const GLenum* /* storeops */) {}
 void GLES2InterfaceStub::PixelLocalStorageBarrierANGLE() {}
+void GLES2InterfaceStub::FramebufferPixelLocalStorageInterruptANGLE() {}
+void GLES2InterfaceStub::FramebufferPixelLocalStorageRestoreANGLE() {}
 void GLES2InterfaceStub::GetFramebufferPixelLocalStorageParameterfvANGLE(
     GLint /* plane */,
     GLenum /* pname */,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
index 3bb8137..db2feec1 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -906,6 +906,8 @@
 void BeginPixelLocalStorageANGLE(GLsizei count, const GLenum* loadops) override;
 void EndPixelLocalStorageANGLE(GLsizei count, const GLenum* storeops) override;
 void PixelLocalStorageBarrierANGLE() override;
+void FramebufferPixelLocalStorageInterruptANGLE() override;
+void FramebufferPixelLocalStorageRestoreANGLE() override;
 void GetFramebufferPixelLocalStorageParameterfvANGLE(GLint plane,
                                                      GLenum pname,
                                                      GLfloat* params) override;
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
index 915fe02..0da0851a 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -2603,6 +2603,18 @@
   gl_->PixelLocalStorageBarrierANGLE();
 }
 
+void GLES2TraceImplementation::FramebufferPixelLocalStorageInterruptANGLE() {
+  TRACE_EVENT_BINARY_EFFICIENT0(
+      "gpu", "GLES2Trace::FramebufferPixelLocalStorageInterruptANGLE");
+  gl_->FramebufferPixelLocalStorageInterruptANGLE();
+}
+
+void GLES2TraceImplementation::FramebufferPixelLocalStorageRestoreANGLE() {
+  TRACE_EVENT_BINARY_EFFICIENT0(
+      "gpu", "GLES2Trace::FramebufferPixelLocalStorageRestoreANGLE");
+  gl_->FramebufferPixelLocalStorageRestoreANGLE();
+}
+
 void GLES2TraceImplementation::GetFramebufferPixelLocalStorageParameterfvANGLE(
     GLint plane,
     GLenum pname,
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index 80d2fe78..8687b889 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -16759,6 +16759,62 @@
 static_assert(offsetof(PixelLocalStorageBarrierANGLE, header) == 0,
               "offset of PixelLocalStorageBarrierANGLE header should be 0");
 
+struct FramebufferPixelLocalStorageInterruptANGLE {
+  typedef FramebufferPixelLocalStorageInterruptANGLE ValueType;
+  static const CommandId kCmdId = kFramebufferPixelLocalStorageInterruptANGLE;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init() { SetHeader(); }
+
+  void* Set(void* cmd) {
+    static_cast<ValueType*>(cmd)->Init();
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+};
+
+static_assert(sizeof(FramebufferPixelLocalStorageInterruptANGLE) == 4,
+              "size of FramebufferPixelLocalStorageInterruptANGLE should be 4");
+static_assert(
+    offsetof(FramebufferPixelLocalStorageInterruptANGLE, header) == 0,
+    "offset of FramebufferPixelLocalStorageInterruptANGLE header should be 0");
+
+struct FramebufferPixelLocalStorageRestoreANGLE {
+  typedef FramebufferPixelLocalStorageRestoreANGLE ValueType;
+  static const CommandId kCmdId = kFramebufferPixelLocalStorageRestoreANGLE;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init() { SetHeader(); }
+
+  void* Set(void* cmd) {
+    static_cast<ValueType*>(cmd)->Init();
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+};
+
+static_assert(sizeof(FramebufferPixelLocalStorageRestoreANGLE) == 4,
+              "size of FramebufferPixelLocalStorageRestoreANGLE should be 4");
+static_assert(
+    offsetof(FramebufferPixelLocalStorageRestoreANGLE, header) == 0,
+    "offset of FramebufferPixelLocalStorageRestoreANGLE header should be 0");
+
 struct GetFramebufferPixelLocalStorageParameterfvANGLE {
   typedef GetFramebufferPixelLocalStorageParameterfvANGLE ValueType;
   static const CommandId kCmdId =
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index fb773ca..1eccc4f 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -5835,6 +5835,28 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
+TEST_F(GLES2FormatTest, FramebufferPixelLocalStorageInterruptANGLE) {
+  cmds::FramebufferPixelLocalStorageInterruptANGLE& cmd =
+      *GetBufferAs<cmds::FramebufferPixelLocalStorageInterruptANGLE>();
+  void* next_cmd = cmd.Set(&cmd);
+  EXPECT_EQ(static_cast<uint32_t>(
+                cmds::FramebufferPixelLocalStorageInterruptANGLE::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
+TEST_F(GLES2FormatTest, FramebufferPixelLocalStorageRestoreANGLE) {
+  cmds::FramebufferPixelLocalStorageRestoreANGLE& cmd =
+      *GetBufferAs<cmds::FramebufferPixelLocalStorageRestoreANGLE>();
+  void* next_cmd = cmd.Set(&cmd);
+  EXPECT_EQ(static_cast<uint32_t>(
+                cmds::FramebufferPixelLocalStorageRestoreANGLE::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
 TEST_F(GLES2FormatTest, GetFramebufferPixelLocalStorageParameterfvANGLE) {
   cmds::GetFramebufferPixelLocalStorageParameterfvANGLE& cmd =
       *GetBufferAs<cmds::GetFramebufferPixelLocalStorageParameterfvANGLE>();
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index f20fe79..d14a63a 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -349,8 +349,10 @@
   OP(BeginPixelLocalStorageANGLEImmediate)                     /* 590 */ \
   OP(EndPixelLocalStorageANGLEImmediate)                       /* 591 */ \
   OP(PixelLocalStorageBarrierANGLE)                            /* 592 */ \
-  OP(GetFramebufferPixelLocalStorageParameterfvANGLE)          /* 593 */ \
-  OP(GetFramebufferPixelLocalStorageParameterivANGLE)          /* 594 */
+  OP(FramebufferPixelLocalStorageInterruptANGLE)               /* 593 */ \
+  OP(FramebufferPixelLocalStorageRestoreANGLE)                 /* 594 */ \
+  OP(GetFramebufferPixelLocalStorageParameterfvANGLE)          /* 595 */ \
+  OP(GetFramebufferPixelLocalStorageParameterivANGLE)          /* 596 */
 
 enum CommandId {
   kOneBeforeStartPoint =
diff --git a/gpu/command_buffer/gles2_cmd_buffer_functions.txt b/gpu/command_buffer/gles2_cmd_buffer_functions.txt
index e960fba..97280f8 100644
--- a/gpu/command_buffer/gles2_cmd_buffer_functions.txt
+++ b/gpu/command_buffer/gles2_cmd_buffer_functions.txt
@@ -406,5 +406,7 @@
 GL_APICALL void         GL_APIENTRY glBeginPixelLocalStorageANGLE (GLsizei count, const GLenum* loadops);
 GL_APICALL void         GL_APIENTRY glEndPixelLocalStorageANGLE (GLsizei count, const GLenum* storeops);
 GL_APICALL void         GL_APIENTRY glPixelLocalStorageBarrierANGLE (void);
+GL_APICALL void         GL_APIENTRY glFramebufferPixelLocalStorageInterruptANGLE (void);
+GL_APICALL void         GL_APIENTRY glFramebufferPixelLocalStorageRestoreANGLE (void);
 GL_APICALL void         GL_APIENTRY glGetFramebufferPixelLocalStorageParameterfvANGLE (GLint plane, GLenum pname, GLfloat* params);
 GL_APICALL void         GL_APIENTRY glGetFramebufferPixelLocalStorageParameterivANGLE (GLint plane, GLenum pname, GLint* params);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index fb425a2..bec9086 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -1313,6 +1313,8 @@
   void DoBeginPixelLocalStorageANGLE(GLsizei n, const volatile GLenum* loadops);
   void DoEndPixelLocalStorageANGLE(GLsizei n, const volatile GLenum* storeops);
   void DoPixelLocalStorageBarrierANGLE();
+  void DoFramebufferPixelLocalStorageInterruptANGLE();
+  void DoFramebufferPixelLocalStorageRestoreANGLE();
   void DoGetFramebufferPixelLocalStorageParameterfvANGLE(GLint plane,
                                                          GLenum pname,
                                                          GLfloat* params,
@@ -19334,6 +19336,14 @@
   NOTIMPLEMENTED();
 }
 
+void GLES2DecoderImpl::DoFramebufferPixelLocalStorageInterruptANGLE() {
+  NOTIMPLEMENTED();
+}
+
+void GLES2DecoderImpl::DoFramebufferPixelLocalStorageRestoreANGLE() {
+  NOTIMPLEMENTED();
+}
+
 void GLES2DecoderImpl::DoGetFramebufferPixelLocalStorageParameterfvANGLE(
     GLint plane,
     GLenum pname,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index a733ac0..6fed48e 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -5803,6 +5803,32 @@
   return error::kNoError;
 }
 
+error::Error GLES2DecoderImpl::HandleFramebufferPixelLocalStorageInterruptANGLE(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  if (!feature_info_->IsWebGL2OrES3OrHigherContext())
+    return error::kUnknownCommand;
+  if (!features().angle_shader_pixel_local_storage) {
+    return error::kUnknownCommand;
+  }
+
+  DoFramebufferPixelLocalStorageInterruptANGLE();
+  return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleFramebufferPixelLocalStorageRestoreANGLE(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  if (!feature_info_->IsWebGL2OrES3OrHigherContext())
+    return error::kUnknownCommand;
+  if (!features().angle_shader_pixel_local_storage) {
+    return error::kUnknownCommand;
+  }
+
+  DoFramebufferPixelLocalStorageRestoreANGLE();
+  return error::kNoError;
+}
+
 error::Error
 GLES2DecoderImpl::HandleGetFramebufferPixelLocalStorageParameterfvANGLE(
     uint32_t immediate_data_size,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index c48b0e2..3956ad8 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -216,8 +216,8 @@
                         GLsizei samples,
                         GLenum internal_format) {
   gl::GLApi* api = impl->api();
-  GLES2DecoderPassthroughImpl::ScopedPixelLocalStorageDeactivate
-      scoped_pls_deactivate(impl);
+  GLES2DecoderPassthroughImpl::ScopedPixelLocalStorageInterrupt
+      scoped_pls_interrupt(impl);
   ScopedRenderbufferBindingReset scoped_renderbuffer_reset(api);
 
   api->glBindRenderbufferEXTFn(GL_RENDERBUFFER, renderbuffer);
@@ -313,38 +313,19 @@
 
 }  // anonymous namespace
 
-void GLES2DecoderPassthroughImpl::ScopedPixelLocalStorageDeactivate::
-    StashIfNeeded(gl::GLApi* api, GLint active_plane_count) {
-  if (active_plane_count != 0) {
-    DCHECK_LE(active_plane_count, kPassthroughMaxPLSPlanes);
-    active_plane_count = std::min(active_plane_count, kPassthroughMaxPLSPlanes);
-    GLenum storeops[kPassthroughMaxPLSPlanes];
-    std::fill(storeops, storeops + active_plane_count, GL_STORE_OP_STORE_ANGLE);
-    api->glEndPixelLocalStorageANGLEFn(active_plane_count, storeops);
-  }
-}
-
-void GLES2DecoderPassthroughImpl::ScopedPixelLocalStorageDeactivate::
-    RestoreIfNeeded(gl::GLApi* api, int stashed_plane_count) {
-  if (stashed_plane_count != 0) {
-    DCHECK_LE(stashed_plane_count, kPassthroughMaxPLSPlanes);
-    stashed_plane_count =
-        std::min(stashed_plane_count, kPassthroughMaxPLSPlanes);
-    GLenum loadops[kPassthroughMaxPLSPlanes];
-    std::fill(loadops, loadops + stashed_plane_count, GL_LOAD_OP_LOAD_ANGLE);
-    api->glBeginPixelLocalStorageANGLEFn(stashed_plane_count, loadops);
-  }
-}
-
-GLES2DecoderPassthroughImpl::ScopedPixelLocalStorageDeactivate::
-    ScopedPixelLocalStorageDeactivate(const GLES2DecoderPassthroughImpl* impl)
+GLES2DecoderPassthroughImpl::ScopedPixelLocalStorageInterrupt::
+    ScopedPixelLocalStorageInterrupt(const GLES2DecoderPassthroughImpl* impl)
     : impl_(impl) {
-  StashIfNeeded(impl_->api(), impl_->active_pls_plane_count_);
+  if (impl_->has_activated_pixel_local_storage_) {
+    impl_->api()->glFramebufferPixelLocalStorageInterruptANGLEFn();
+  }
 }
 
-GLES2DecoderPassthroughImpl::ScopedPixelLocalStorageDeactivate::
-    ~ScopedPixelLocalStorageDeactivate() {
-  RestoreIfNeeded(impl_->api(), impl_->active_pls_plane_count_);
+GLES2DecoderPassthroughImpl::ScopedPixelLocalStorageInterrupt::
+    ~ScopedPixelLocalStorageInterrupt() {
+  if (impl_->has_activated_pixel_local_storage_) {
+    impl_->api()->glFramebufferPixelLocalStorageRestoreANGLEFn();
+  }
 }
 
 GLES2DecoderPassthroughImpl::TexturePendingBinding::TexturePendingBinding(
@@ -517,8 +498,8 @@
 
     // Back up all state we are about to change.
     gl::GLApi* api = impl->api();
-    GLES2DecoderPassthroughImpl::ScopedPixelLocalStorageDeactivate
-        scoped_pls_deactivate(impl);
+    GLES2DecoderPassthroughImpl::ScopedPixelLocalStorageInterrupt
+        scoped_pls_interrupt(impl);
     ScopedFramebufferBindingReset fbo_reset(
         api, false /* supports_seperate_fbo_bindings */);
     ScopedTextureBindingReset texture_reset(api, texture->target());
@@ -647,7 +628,7 @@
 GLES2DecoderPassthroughImpl::EmulatedColorBuffer::EmulatedColorBuffer(
     const GLES2DecoderPassthroughImpl* impl)
     : impl_(impl) {
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(impl_);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(impl_);
   ScopedTextureBindingReset scoped_texture_reset(api(), GL_TEXTURE_2D);
 
   GLuint color_buffer_texture = 0;
@@ -670,7 +651,7 @@
   }
   size = new_size;
 
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(impl_);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(impl_);
   ScopedTextureBindingReset scoped_texture_reset(api(), GL_TEXTURE_2D);
 
   DCHECK(texture);
@@ -697,7 +678,7 @@
 GLES2DecoderPassthroughImpl::EmulatedDefaultFramebuffer::
     EmulatedDefaultFramebuffer(const GLES2DecoderPassthroughImpl* impl)
     : impl_(impl) {
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(impl_);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(impl_);
   ScopedFramebufferBindingReset scoped_fbo_reset(
       api(), impl_->supports_separate_fbo_bindings_);
   ScopedRenderbufferBindingReset scoped_renderbuffer_reset(api());
@@ -772,7 +753,7 @@
   color_texture = std::move(new_color_buffer);
 
   // Bind the new texture to this FBO
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(impl_);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(impl_);
   ScopedFramebufferBindingReset scoped_fbo_reset(
       api(), impl_->supports_separate_fbo_bindings_);
   api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, framebuffer_service_id);
@@ -788,7 +769,7 @@
   DCHECK(target != nullptr);
   DCHECK_EQ(target->size, size);
 
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(impl_);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(impl_);
   ScopedFramebufferBindingReset scoped_fbo_reset(
       api(), impl_->supports_separate_fbo_bindings_);
 
@@ -840,7 +821,7 @@
 
   // Check that the framebuffer is complete
   {
-    ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(impl_);
+    ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(impl_);
     ScopedFramebufferBindingReset scoped_fbo_reset(
         api(), impl_->supports_separate_fbo_bindings_);
     api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, framebuffer_service_id);
@@ -2158,13 +2139,15 @@
     it->second.command_processing_start_time = base::TimeTicks::Now();
   }
 
-  ScopedPixelLocalStorageDeactivate::RestoreIfNeeded(api(),
-                                                     active_pls_plane_count_);
+  if (has_activated_pixel_local_storage_) {
+    api()->glFramebufferPixelLocalStorageRestoreANGLEFn();
+  }
 }
 
 void GLES2DecoderPassthroughImpl::EndDecoding() {
-  ScopedPixelLocalStorageDeactivate::StashIfNeeded(api(),
-                                                   active_pls_plane_count_);
+  if (has_activated_pixel_local_storage_) {
+    api()->glFramebufferPixelLocalStorageInterruptANGLEFn();
+  }
 
 #if BUILDFLAG(IS_WIN)
   resources_->SuspendSharedImageAccessIfNeeded();
@@ -2620,7 +2603,7 @@
 
 GLES2DecoderPassthroughImpl::LazySharedContextState::~LazySharedContextState() {
   if (shared_context_state_) {
-    ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(impl_);
+    ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(impl_);
     ui::ScopedMakeCurrent smc(shared_context_state_->context(),
                               shared_context_state_->surface());
     shared_context_state_.reset();
@@ -2659,7 +2642,7 @@
   }
 
   // Make current context using `gl_context` and `gl_surface`
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(impl_);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(impl_);
   ui::ScopedMakeCurrent smc(gl_context.get(), gl_surface.get());
 
   ContextGroup* group = impl_->GetContextGroup();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
index 7d650a8..4cb95db 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
@@ -428,13 +428,10 @@
     return feature_info_->feature_flags();
   }
 
-  class ScopedPixelLocalStorageDeactivate {
+  class ScopedPixelLocalStorageInterrupt {
    public:
-    static void StashIfNeeded(gl::GLApi*, GLint active_plane_count);
-    static void RestoreIfNeeded(gl::GLApi* api, GLint stashed_plane_count);
-
-    ScopedPixelLocalStorageDeactivate(const GLES2DecoderPassthroughImpl*);
-    ~ScopedPixelLocalStorageDeactivate();
+    ScopedPixelLocalStorageInterrupt(const GLES2DecoderPassthroughImpl*);
+    ~ScopedPixelLocalStorageInterrupt();
 
    private:
     raw_ptr<const GLES2DecoderPassthroughImpl> impl_;
@@ -965,9 +962,9 @@
   // If this context supports both read and draw framebuffer bindings
   bool supports_separate_fbo_bindings_ = false;
 
-  // Tracks the number of ANGLE_shader_pixel_local_storage planes to turn off
-  // and back on in the event that a rendering pass gets interrupted.
-  GLint active_pls_plane_count_ = 0;
+  // Tracks if the context has ever called glBeginPixelLocalStorageANGLE. If it
+  // has, we need to start using the pixel local storage interrupt mechanism.
+  bool has_activated_pixel_local_storage_ = false;
 
   // Tracing
   std::unique_ptr<GPUTracer> gpu_tracer_;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
index 0a66fc2f..34ab38bf 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
@@ -1108,6 +1108,8 @@
 error::Error DoEndPixelLocalStorageANGLE(GLsizei n,
                                          const volatile GLenum* storeops);
 error::Error DoPixelLocalStorageBarrierANGLE();
+error::Error DoFramebufferPixelLocalStorageInterruptANGLE();
+error::Error DoFramebufferPixelLocalStorageRestoreANGLE();
 error::Error DoGetFramebufferPixelLocalStorageParameterfvANGLE(GLint plane,
                                                                GLenum pname,
                                                                GLsizei bufsize,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index c23cd37..eb222f2 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -2512,7 +2512,7 @@
       return error::kNoError;
     }
   }
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(this);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(this);
   ui::ScopedMakeCurrent smc(lazy_context_->shared_context_state()->context(),
                             lazy_context_->shared_context_state()->surface());
 
@@ -5119,7 +5119,7 @@
       return error::kNoError;
     }
   }
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(this);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(this);
   ui::ScopedMakeCurrent smc(lazy_context_->shared_context_state()->context(),
                             lazy_context_->shared_context_state()->surface());
   CopySharedImageHelper helper(group_->shared_image_representation_factory(),
@@ -5143,7 +5143,7 @@
       return error::kNoError;
     }
   }
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(this);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(this);
   ui::ScopedMakeCurrent smc(lazy_context_->shared_context_state()->context(),
                             lazy_context_->shared_context_state()->surface());
   CopySharedImageHelper helper(group_->shared_image_representation_factory(),
@@ -5171,7 +5171,7 @@
       return error::kNoError;
     }
   }
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(this);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(this);
   ui::ScopedMakeCurrent smc(lazy_context_->shared_context_state()->context(),
                             lazy_context_->shared_context_state()->surface());
   CopySharedImageHelper helper(group_->shared_image_representation_factory(),
@@ -5201,7 +5201,7 @@
       return error::kNoError;
     }
   }
-  ScopedPixelLocalStorageDeactivate scoped_pls_deactivate(this);
+  ScopedPixelLocalStorageInterrupt scoped_pls_interrupt(this);
   ui::ScopedMakeCurrent smc(lazy_context_->shared_context_state()->context(),
                             lazy_context_->shared_context_state()->surface());
 
@@ -5333,10 +5333,7 @@
   GLenum loadops_copy[kPassthroughMaxPLSPlanes];
   std::copy(loadops, loadops + n, loadops_copy);
   api()->glBeginPixelLocalStorageANGLEFn(n, loadops_copy);
-  // Query GL_PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_ANGLE in case there was an error
-  // and the number of active planes isn't actually <n>.
-  api()->glGetIntegervFn(GL_PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_ANGLE,
-                         &active_pls_plane_count_);
+  has_activated_pixel_local_storage_ = true;
   return error::kNoError;
 }
 
@@ -5360,10 +5357,6 @@
   GLenum storeops_copy[kPassthroughMaxPLSPlanes];
   std::copy(storeops, storeops + n, storeops_copy);
   api()->glEndPixelLocalStorageANGLEFn(n, storeops_copy);
-  // Query GL_PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_ANGLE in case there was an error
-  // and the number of active planes isn't actually zero.
-  api()->glGetIntegervFn(GL_PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_ANGLE,
-                         &active_pls_plane_count_);
   return error::kNoError;
 }
 
@@ -5377,6 +5370,24 @@
 }
 
 error::Error
+GLES2DecoderPassthroughImpl::DoFramebufferPixelLocalStorageInterruptANGLE() {
+  if (IsEmulatedFramebufferBound(GL_DRAW_FRAMEBUFFER)) {
+    return error::kNoError;
+  }
+  api()->glFramebufferPixelLocalStorageInterruptANGLEFn();
+  return error::kNoError;
+}
+
+error::Error
+GLES2DecoderPassthroughImpl::DoFramebufferPixelLocalStorageRestoreANGLE() {
+  if (IsEmulatedFramebufferBound(GL_DRAW_FRAMEBUFFER)) {
+    return error::kNoError;
+  }
+  api()->glFramebufferPixelLocalStorageRestoreANGLEFn();
+  return error::kNoError;
+}
+
+error::Error
 GLES2DecoderPassthroughImpl::DoGetFramebufferPixelLocalStorageParameterfvANGLE(
     GLint plane,
     GLenum pname,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
index 3fd480e..58607a2 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
@@ -5130,6 +5130,40 @@
   return error::kNoError;
 }
 
+error::Error
+GLES2DecoderPassthroughImpl::HandleFramebufferPixelLocalStorageInterruptANGLE(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  if (!feature_info_->IsWebGL2OrES3OrHigherContext())
+    return error::kUnknownCommand;
+  if (!features().angle_shader_pixel_local_storage) {
+    return error::kUnknownCommand;
+  }
+
+  error::Error error = DoFramebufferPixelLocalStorageInterruptANGLE();
+  if (error != error::kNoError) {
+    return error;
+  }
+  return error::kNoError;
+}
+
+error::Error
+GLES2DecoderPassthroughImpl::HandleFramebufferPixelLocalStorageRestoreANGLE(
+    uint32_t immediate_data_size,
+    const volatile void* cmd_data) {
+  if (!feature_info_->IsWebGL2OrES3OrHigherContext())
+    return error::kUnknownCommand;
+  if (!features().angle_shader_pixel_local_storage) {
+    return error::kUnknownCommand;
+  }
+
+  error::Error error = DoFramebufferPixelLocalStorageRestoreANGLE();
+  if (error != error::kNoError) {
+    return error;
+  }
+  return error::kNoError;
+}
+
 error::Error GLES2DecoderPassthroughImpl::
     HandleGetFramebufferPixelLocalStorageParameterfvANGLE(
         uint32_t immediate_data_size,
diff --git "a/infra/config/generated/builders/ci/Mac Builder \050dbg\051/properties.json" "b/infra/config/generated/builders/ci/Mac Builder \050dbg\051/properties.json"
index b5a00e6..6616f414 100644
--- "a/infra/config/generated/builders/ci/Mac Builder \050dbg\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Mac Builder \050dbg\051/properties.json"
@@ -55,36 +55,6 @@
                 "project": "chromium"
               }
             }
-          },
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "mac-osxbeta-rel",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-fyi-archive",
-              "builder_group": "chromium.fyi",
-              "execution_mode": "TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb",
-                  "goma_use_local"
-                ],
-                "build_config": "Release",
-                "config": "chromium",
-                "target_bits": 64,
-                "target_platform": "mac"
-              },
-              "legacy_gclient_config": {
-                "config": "chromium"
-              },
-              "parent": {
-                "bucket": "ci",
-                "builder": "Mac Builder (dbg)",
-                "project": "chromium"
-              }
-            }
           }
         ]
       },
@@ -100,19 +70,10 @@
           "bucket": "ci",
           "builder": "Mac12 Tests (dbg)",
           "project": "chromium"
-        },
-        {
-          "bucket": "ci",
-          "builder": "mac-osxbeta-rel",
-          "project": "chromium"
         }
       ],
       "mirroring_builder_group_and_names": [
         {
-          "builder": "mac-osxbeta-rel",
-          "group": "tryserver.chromium.mac"
-        },
-        {
           "builder": "mac_chromium_compile_dbg_ng",
           "group": "tryserver.chromium.mac"
         },
diff --git a/infra/config/generated/builders/ci/mac-osxbeta-rel/properties.json b/infra/config/generated/builders/ci/mac-osxbeta-rel/properties.json
index ce7b923..d5a3d73 100644
--- a/infra/config/generated/builders/ci/mac-osxbeta-rel/properties.json
+++ b/infra/config/generated/builders/ci/mac-osxbeta-rel/properties.json
@@ -6,37 +6,13 @@
           {
             "builder_id": {
               "bucket": "ci",
-              "builder": "Mac Builder (dbg)",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-mac-archive",
-              "builder_group": "chromium.mac",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Debug",
-                "config": "chromium",
-                "target_bits": 64,
-                "target_platform": "mac"
-              },
-              "legacy_gclient_config": {
-                "config": "chromium"
-              }
-            }
-          },
-          {
-            "builder_id": {
-              "bucket": "ci",
               "builder": "mac-osxbeta-rel",
               "project": "chromium"
             },
             "builder_spec": {
               "build_gs_bucket": "chromium-fyi-archive",
               "builder_group": "chromium.fyi",
-              "execution_mode": "TEST",
+              "execution_mode": "COMPILE_AND_TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
                   "mb",
@@ -49,11 +25,6 @@
               },
               "legacy_gclient_config": {
                 "config": "chromium"
-              },
-              "parent": {
-                "bucket": "ci",
-                "builder": "Mac Builder (dbg)",
-                "project": "chromium"
               }
             }
           }
@@ -70,14 +41,16 @@
         {
           "builder": "mac-osxbeta-rel",
           "group": "tryserver.chromium.mac"
-        },
-        {
-          "builder": "mac_chromium_compile_dbg_ng",
-          "group": "tryserver.chromium.mac"
         }
       ]
     }
   },
+  "$build/reclient": {
+    "instance": "rbe-chromium-trusted",
+    "jobs": 250,
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
   "$recipe_engine/resultdb/test_presentation": {
     "column_keys": [],
     "grouping_keys": [
diff --git a/infra/config/generated/builders/try/mac-osxbeta-rel/properties.json b/infra/config/generated/builders/try/mac-osxbeta-rel/properties.json
index 8b1fed9..c70ba3a 100644
--- a/infra/config/generated/builders/try/mac-osxbeta-rel/properties.json
+++ b/infra/config/generated/builders/try/mac-osxbeta-rel/properties.json
@@ -6,37 +6,13 @@
           {
             "builder_id": {
               "bucket": "ci",
-              "builder": "Mac Builder (dbg)",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-mac-archive",
-              "builder_group": "chromium.mac",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Debug",
-                "config": "chromium",
-                "target_bits": 64,
-                "target_platform": "mac"
-              },
-              "legacy_gclient_config": {
-                "config": "chromium"
-              }
-            }
-          },
-          {
-            "builder_id": {
-              "bucket": "ci",
               "builder": "mac-osxbeta-rel",
               "project": "chromium"
             },
             "builder_spec": {
               "build_gs_bucket": "chromium-fyi-archive",
               "builder_group": "chromium.fyi",
-              "execution_mode": "TEST",
+              "execution_mode": "COMPILE_AND_TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
                   "mb",
@@ -49,11 +25,6 @@
               },
               "legacy_gclient_config": {
                 "config": "chromium"
-              },
-              "parent": {
-                "bucket": "ci",
-                "builder": "Mac Builder (dbg)",
-                "project": "chromium"
               }
             }
           }
@@ -62,13 +33,6 @@
       "builder_ids": [
         {
           "bucket": "ci",
-          "builder": "Mac Builder (dbg)",
-          "project": "chromium"
-        }
-      ],
-      "builder_ids_in_scope_for_testing": [
-        {
-          "bucket": "ci",
           "builder": "mac-osxbeta-rel",
           "project": "chromium"
         }
diff --git a/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/properties.json b/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/properties.json
index d92dc124..2852506 100644
--- a/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/properties.json
+++ b/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/properties.json
@@ -55,36 +55,6 @@
                 "project": "chromium"
               }
             }
-          },
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "mac-osxbeta-rel",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-fyi-archive",
-              "builder_group": "chromium.fyi",
-              "execution_mode": "TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb",
-                  "goma_use_local"
-                ],
-                "build_config": "Release",
-                "config": "chromium",
-                "target_bits": 64,
-                "target_platform": "mac"
-              },
-              "legacy_gclient_config": {
-                "config": "chromium"
-              },
-              "parent": {
-                "bucket": "ci",
-                "builder": "Mac Builder (dbg)",
-                "project": "chromium"
-              }
-            }
           }
         ]
       },
@@ -100,11 +70,6 @@
           "bucket": "ci",
           "builder": "Mac12 Tests (dbg)",
           "project": "chromium"
-        },
-        {
-          "bucket": "ci",
-          "builder": "mac-osxbeta-rel",
-          "project": "chromium"
         }
       ],
       "is_compile_only": true
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index ed1682f..8ad4ca82 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -6558,6 +6558,7 @@
   triggers: "mac-backuprefptr-x64-fyi-rel"
   triggers: "mac-hermetic-upgrade-rel"
   triggers: "mac-official"
+  triggers: "mac-osxbeta-rel"
   triggers: "mac-rel-cft"
   triggers: "mac-rust-x64-rel"
   triggers: "mac-swangle-chromium-x64"
diff --git a/infra/config/generated/luci/realms.cfg b/infra/config/generated/luci/realms.cfg
index 6f2ae2e..c6d146d 100644
--- a/infra/config/generated/luci/realms.cfg
+++ b/infra/config/generated/luci/realms.cfg
@@ -164,7 +164,6 @@
         values: "linux-updater-tester-dbg"
         values: "linux-updater-tester-rel"
         values: "mac-fieldtrial-tester"
-        values: "mac-osxbeta-rel"
         values: "mac10.13-updater-tester-dbg"
         values: "mac10.13-updater-tester-rel"
         values: "mac10.14-updater-tester-dbg"
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index 96370e9c..1c6e550 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -904,13 +904,9 @@
     ),
 )
 
-# This is launching & collecting entirely isolated tests.
-# OS shouldn't matter.
-ci.thin_tester(
+ci.builder(
     name = "mac-osxbeta-rel",
-    triggered_by = ["ci/Mac Builder (dbg)"],
     builder_spec = builder_config.builder_spec(
-        execution_mode = builder_config.execution_mode.TEST,
         gclient_config = builder_config.gclient_config(
             config = "chromium",
         ),
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index ad4d30d..a136ba3 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -52,7 +52,6 @@
 try_.builder(
     name = "mac-osxbeta-rel",
     mirrors = [
-        "ci/Mac Builder (dbg)",
         "ci/mac-osxbeta-rel",
     ],
     builderless = False,
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 7051d0f4..933fdf5b 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -2859,6 +2859,15 @@
       <message name="IDS_IOS_WEAK_PASSWORD_ISSUES_DESCRIPTION" desc="Header displayed on the password manager screen displaying credentials weak passwords [iOS only]" meaning="This header explains the risks of having weak passwords and provides a link to the support page explaining how create strong passwords.">
         Weak passwords are easy to guess. Make sure you’re creating strong passwords. <ph name="BEGIN_LINK">BEGIN_LINK</ph>See more security tips.<ph name="END_LINK">END_LINK</ph>
       </message>
+      <message name="IDS_IOS_REUSED_PASSWORD_ISSUES_GROUP_HEADER" desc="Header shown on top of a group of credentials sharing the same password, inside the password manager screen diplaying shared passswords. User can only have 2 or more reused passwords, so this string will always be plural. [iOS only]">
+        <ph name="COUNT">$1<ex>2</ex></ph> accounts using same password
+      </message>
+      <message name="IDS_IOS_COMPROMISED_PASSWORD_ISSUES_LEAKED_DESCRIPTION" desc="Text explaining the user one of their credentials was found in a data breach. Displayed on the password manager screen showing credentials that have been leaked or phished. [iOS only]">
+        Found in data breach
+      </message>
+      <message name="IDS_IOS_COMPROMISED_PASSWORD_ISSUES_PHISHED_DESCRIPTION" desc="Text explaining the user one of their credentials was phished. Displayed on the password manager screen showing credentials that have been leaked or phished. [iOS only]">
+        Entered on deceptive site
+      </message>
       <message name="IDS_IOS_CHANGE_COMPROMISED_PASSWORD" desc="Button which is shown on Password Details Screen when password is compromised. [iOS only]" meaning="Telling user to change password on a website since it is compromised.">
         Change Password on Website
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_COMPROMISED_PASSWORD_ISSUES_LEAKED_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_COMPROMISED_PASSWORD_ISSUES_LEAKED_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..aca04c9b
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_COMPROMISED_PASSWORD_ISSUES_LEAKED_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+a7886b664e8d19953a5e9eded9478191b379f707
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_COMPROMISED_PASSWORD_ISSUES_PHISHED_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_COMPROMISED_PASSWORD_ISSUES_PHISHED_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..aca04c9b
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_COMPROMISED_PASSWORD_ISSUES_PHISHED_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+a7886b664e8d19953a5e9eded9478191b379f707
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_REUSED_PASSWORD_ISSUES_GROUP_HEADER.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_REUSED_PASSWORD_ISSUES_GROUP_HEADER.png.sha1
new file mode 100644
index 0000000..690d98f
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_REUSED_PASSWORD_ISSUES_GROUP_HEADER.png.sha1
@@ -0,0 +1 @@
+54265578927d0cec01086e41bc9d2889ddb2c968
\ No newline at end of file
diff --git a/ios/chrome/browser/autocomplete/omnibox_pedal_implementation.mm b/ios/chrome/browser/autocomplete/omnibox_pedal_implementation.mm
index 5cdeb33a..878bdc2 100644
--- a/ios/chrome/browser/autocomplete/omnibox_pedal_implementation.mm
+++ b/ios/chrome/browser/autocomplete/omnibox_pedal_implementation.mm
@@ -72,7 +72,8 @@
   // This method override enables this Pedal to spoof its ID for metrics
   // reporting, making it possible to distinguish incognito usage.
   OmniboxPedalId GetMetricsId() const override {
-    return incognito_ ? OmniboxPedalId::INCOGNITO_CLEAR_BROWSING_DATA : id();
+    return incognito_ ? OmniboxPedalId::INCOGNITO_CLEAR_BROWSING_DATA
+                      : PedalId();
   }
 
  protected:
@@ -465,7 +466,8 @@
 GetPedalImplementations(bool incognito, bool testing) {
   std::unordered_map<OmniboxPedalId, scoped_refptr<OmniboxPedal>> pedals;
   __unused const auto add = [&](OmniboxPedal* pedal) {
-    pedals.insert(std::make_pair(pedal->id(), base::WrapRefCounted(pedal)));
+    pedals.insert(
+        std::make_pair(pedal->PedalId(), base::WrapRefCounted(pedal)));
   };
 
   if (!incognito) {
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm
index 6883072d..49b8f309 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_pedal_annotator.mm
@@ -44,26 +44,22 @@
   // predicate or iterating through `match.actions`. In that case,
   // the static_casts below should also be removed in favor of generic
   // use of the OmniboxAction base class.
-  const OmniboxAction* pedalAction =
-      match.GetActionWhere([](const auto& action) {
-        // Pedal action is a match action with an ID in
-        // 0 ..< OmniboxPedalId::TOTAL_COUNT range (and ID 0 is NONE).
-        return action->GetID() < static_cast<int>(OmniboxPedalId::TOTAL_COUNT);
-      });
-  if (!pedalAction) {
+  const OmniboxPedal* omniboxPedal =
+      OmniboxPedal::FromAction(match.GetActionWhere([](const auto& action) {
+        return action->ActionId() == OmniboxActionId::PEDAL;
+      }));
+  if (!omniboxPedal) {
     return nil;
   }
   __weak id<ApplicationCommands> pedalsEndpoint = self.pedalsEndpoint;
   __weak id<OmniboxCommands> omniboxCommandHandler = self.omniboxCommandHandler;
 
   NSString* hint =
-      base::SysUTF16ToNSString(pedalAction->GetLabelStrings().hint);
+      base::SysUTF16ToNSString(omniboxPedal->GetLabelStrings().hint);
   NSString* suggestionContents = base::SysUTF16ToNSString(
-      pedalAction->GetLabelStrings().suggestion_contents);
-  NSInteger pedalType = static_cast<NSInteger>(
-      static_cast<const OmniboxPedal*>(pedalAction)->GetMetricsId());
-
-  OmniboxPedalId pedalId = static_cast<OmniboxPedalId>(pedalAction->GetID());
+      omniboxPedal->GetLabelStrings().suggestion_contents);
+  NSInteger pedalType = static_cast<NSInteger>(omniboxPedal->GetMetricsId());
+  OmniboxPedalId pedalId = omniboxPedal->PedalId();
 
   UIImage* image;
 
diff --git a/ios/chrome/browser/ui/settings/password/password_checkup/BUILD.gn b/ios/chrome/browser/ui/settings/password/password_checkup/BUILD.gn
index f99f4db2..2f43baf 100644
--- a/ios/chrome/browser/ui/settings/password/password_checkup/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/password_checkup/BUILD.gn
@@ -19,6 +19,7 @@
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/ui/table_view:utils",
+    "//ios/chrome/browser/ui/settings/password/password_issues",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_commands.h b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_commands.h
index 329122a0..76d10c4 100644
--- a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_commands.h
+++ b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_commands.h
@@ -5,12 +5,21 @@
 #ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_CHECKUP_PASSWORD_CHECKUP_COMMANDS_H_
 #define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_CHECKUP_PASSWORD_CHECKUP_COMMANDS_H_
 
+namespace password_manager {
+enum class WarningType;
+}
+
 // Commands relative to the Password Checkup homepage.
 @protocol PasswordCheckupCommands
 
 // Called when view controller is removed.
 - (void)dismissPasswordCheckupViewController;
 
+// Opens the Password Issues list displaying compromised, weak or reused
+// credentials.
+- (void)showPasswordIssuesWithWarningType:
+    (password_manager::WarningType)warningType;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_CHECKUP_PASSWORD_CHECKUP_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_coordinator.h b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_coordinator.h
index babe3c91..e6f2dc197 100644
--- a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_coordinator.h
+++ b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_coordinator.h
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
 
 @class PasswordCheckupCoordinator;
+@class ReauthenticationModule;
 
 // Delegate for PasswordCheckupCoordinator.
 @protocol PasswordCheckupCoordinatorDelegate
@@ -24,6 +25,8 @@
 - (instancetype)initWithBaseNavigationController:
                     (UINavigationController*)navigationController
                                          browser:(Browser*)browser
+                                    reauthModule:
+                                        (ReauthenticationModule*)reauthModule
     NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
diff --git a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_coordinator.mm b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_coordinator.mm
index d383316b..e7cf1e67 100644
--- a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_coordinator.mm
@@ -11,12 +11,20 @@
 #import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_commands.h"
 #import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_mediator.h"
 #import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_view_controller.h"
+#import "ios/chrome/browser/ui/settings/password/password_issues/password_issues_coordinator.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-@interface PasswordCheckupCoordinator () <PasswordCheckupCommands>
+@interface PasswordCheckupCoordinator () <PasswordCheckupCommands,
+                                          PasswordIssuesCoordinatorDelegate> {
+  // Coordinator for password issues.
+  PasswordIssuesCoordinator* _passwordIssuesCoordinator;
+
+  // Reauthentication module used by password issues coordinator.
+  ReauthenticationModule* _reauthModule;
+}
 
 // Main view controller for this coordinator.
 @property(nonatomic, strong) PasswordCheckupViewController* viewController;
@@ -32,11 +40,14 @@
 
 - (instancetype)initWithBaseNavigationController:
                     (UINavigationController*)navigationController
-                                         browser:(Browser*)browser {
+                                         browser:(Browser*)browser
+                                    reauthModule:
+                                        (ReauthenticationModule*)reauthModule {
   self = [super initWithBaseViewController:navigationController
                                    browser:browser];
   if (self) {
     _baseNavigationController = navigationController;
+    _reauthModule = reauthModule;
   }
   return self;
 }
@@ -61,6 +72,8 @@
   self.mediator = nil;
   self.viewController.handler = nil;
   self.viewController = nil;
+
+  [self stopPasswordIssuesCoordinator];
 }
 
 #pragma mark - PasswordCheckupCommands
@@ -69,4 +82,32 @@
   [self.delegate passwordCheckupCoordinatorDidRemove:self];
 }
 
+- (void)showPasswordIssuesWithWarningType:
+    (password_manager::WarningType)warningType {
+  CHECK(!_passwordIssuesCoordinator);
+  _passwordIssuesCoordinator = [[PasswordIssuesCoordinator alloc]
+            initForWarningType:warningType
+      baseNavigationController:self.baseNavigationController
+                       browser:self.browser];
+  _passwordIssuesCoordinator.delegate = self;
+  _passwordIssuesCoordinator.reauthModule = _reauthModule;
+  [_passwordIssuesCoordinator start];
+}
+
+#pragma mark - PasswordIssuesCoordinatorDelegate
+
+- (void)passwordIssuesCoordinatorDidRemove:
+    (PasswordIssuesCoordinator*)coordinator {
+  CHECK_EQ(_passwordIssuesCoordinator, coordinator);
+  [self stopPasswordIssuesCoordinator];
+}
+
+#pragma mark - Private
+
+- (void)stopPasswordIssuesCoordinator {
+  [_passwordIssuesCoordinator stop];
+  _passwordIssuesCoordinator.delegate = nil;
+  _passwordIssuesCoordinator = nil;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_view_controller.mm
index d73e66633..6d113b3 100644
--- a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_view_controller.mm
@@ -26,6 +26,7 @@
 #endif
 
 using password_manager::InsecurePasswordCounts;
+using password_manager::WarningType;
 
 namespace {
 
@@ -414,8 +415,16 @@
       static_cast<ItemType>([model itemTypeForIndexPath:indexPath]);
   switch (itemType) {
     case ItemTypeCompromisedPasswords:
+      [self.handler showPasswordIssuesWithWarningType:
+                        WarningType::kCompromisedPasswordsWarning];
+      break;
     case ItemTypeReusedPasswords:
+      [self.handler showPasswordIssuesWithWarningType:
+                        WarningType::kReusedPasswordsWarning];
+      break;
     case ItemTypeWeakPasswords:
+      [self.handler
+          showPasswordIssuesWithWarningType:WarningType::kWeakPasswordsWarning];
       break;
     case ItemTypePasswordCheckupTimestamp:
       break;
diff --git a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_view_controller_unittest.mm
index b3c2d115..8aad824d 100644
--- a/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_view_controller_unittest.mm
@@ -23,6 +23,7 @@
 #import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
 #import "ios/chrome/browser/ui/icons/symbols.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_check_item.h"
+#import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_commands.h"
 #import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_constants.h"
 #import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_mediator+private.h"
 #import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_mediator.h"
@@ -31,6 +32,8 @@
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/web/public/test/web_task_environment.h"
 #import "testing/gtest_mac.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
 #import "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -41,6 +44,7 @@
 using password_manager::InsecureType;
 using password_manager::PasswordForm;
 using password_manager::TestPasswordStore;
+using password_manager::WarningType;
 
 // Test fixture for testing PasswordCheckupViewController class.
 class PasswordCheckupViewControllerTest : public ChromeTableViewControllerTest {
@@ -80,6 +84,9 @@
     view_controller.delegate = mediator_;
     mediator_.consumer = view_controller;
 
+    handler_ = OCMStrictProtocolMock(@protocol(PasswordCheckupCommands));
+    view_controller.handler = handler_;
+
     // Add a saved password since Password Checkup is not available when the
     // user doesn't have any saved passwords.
     AddSavedForm();
@@ -252,6 +259,13 @@
         IDS_IOS_PASSWORD_CHECKUP_HOMEPAGE_NO_WEAK_PASSWORDS_SUBTITLE);
   }
 
+  // Simulates a tap on an item in the tableView.
+  void SimulateTap(int index, int section) {
+    [controller() tableView:controller().tableView
+        didSelectRowAtIndexPath:[NSIndexPath indexPathForItem:index
+                                                    inSection:section]];
+  }
+
   void RunUntilIdle() { task_environment_.RunUntilIdle(); }
 
   web::WebTaskEnvironment task_environment_;
@@ -259,6 +273,7 @@
   std::unique_ptr<TestBrowser> browser_;
   PasswordCheckupMediator* mediator_;
   base::test::ScopedFeatureList feature_list;
+  id<PasswordCheckupCommands> handler_;
   // Strings for the insecure types section.
   NSString* compromised_text_;
   NSString* compromised_detail_text_;
@@ -547,3 +562,47 @@
 
   [GetPasswordCheckupViewController() settingsWillBeDismissed];
 }
+
+// Verifies that tapping on the compromised passwords cell tells the handler to
+// show compromised issues.
+TEST_F(PasswordCheckupViewControllerTest,
+       TestTapCompromisedPasswordsNotifiesHandler) {
+  AddSavedInsecureForm(InsecureType::kLeaked);
+  ChangePasswordCheckupHomepageState(PasswordCheckupHomepageStateDone);
+
+  OCMExpect([handler_ showPasswordIssuesWithWarningType:
+                          WarningType::kCompromisedPasswordsWarning]);
+
+  SimulateTap(/*index=*/0, /*section=*/0);
+
+  EXPECT_OCMOCK_VERIFY((id)handler_);
+}
+
+// Verifies that tapping on the reused passwords cell tells the handler to show
+// reused issues.
+TEST_F(PasswordCheckupViewControllerTest,
+       TestTapReusedPasswordsNotifiesHandler) {
+  AddSavedInsecureForm(InsecureType::kReused);
+  ChangePasswordCheckupHomepageState(PasswordCheckupHomepageStateDone);
+
+  OCMExpect([handler_
+      showPasswordIssuesWithWarningType:WarningType::kReusedPasswordsWarning]);
+
+  SimulateTap(/*index=*/1, /*section=*/0);
+
+  EXPECT_OCMOCK_VERIFY((id)handler_);
+}
+
+// Verifies that tapping on the weak passwords cell tells the handler to
+// show weak issues.
+TEST_F(PasswordCheckupViewControllerTest, TestTapWeakPasswordsNotifiesHandler) {
+  AddSavedInsecureForm(InsecureType::kWeak);
+  ChangePasswordCheckupHomepageState(PasswordCheckupHomepageStateDone);
+
+  OCMExpect([handler_
+      showPasswordIssuesWithWarningType:WarningType::kWeakPasswordsWarning]);
+
+  SimulateTap(/*index=*/2, /*section=*/0);
+
+  EXPECT_OCMOCK_VERIFY((id)handler_);
+}
diff --git a/ios/chrome/browser/ui/settings/password/password_issues/BUILD.gn b/ios/chrome/browser/ui/settings/password/password_issues/BUILD.gn
index 0ebeada..5e35f5b 100644
--- a/ios/chrome/browser/ui/settings/password/password_issues/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/password_issues/BUILD.gn
@@ -55,12 +55,14 @@
   deps = [
     "//base",
     "//components/password_manager/core/browser",
+    "//components/password_manager/core/common:features",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/net:crurl",
     "//ios/chrome/browser/shared/ui/table_view",
     "//ios/chrome/browser/shared/ui/table_view/cells",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings/password:password_constants",
+    "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/favicon",
     "//ui/base",
   ]
diff --git a/ios/chrome/browser/ui/settings/password/password_issues/password_issue.h b/ios/chrome/browser/ui/settings/password/password_issues/password_issue.h
index a3fb957..0dddd2d 100644
--- a/ios/chrome/browser/ui/settings/password/password_issues/password_issue.h
+++ b/ios/chrome/browser/ui/settings/password/password_issues/password_issue.h
@@ -22,11 +22,20 @@
 @property(nonatomic, copy, readonly) NSString* website;
 // Associated username.
 @property(nonatomic, copy, readonly) NSString* username;
+// Description of type of compromised credential issue.
+// Nil for non-compromised credentials.
+@property(nonatomic, readonly) NSString* compromisedDescription;
 // Credential being displayed in Password Details screen.
 @property(nonatomic, readonly) password_manager::CredentialUIEntry credential;
 
+// Initializes a PasswordIssue from a CredentialUIEntry.
+// Pass `enableCompromisedDescription` as YES when the description of
+// compromised issues should be displayed in the UX (e.g., When displaying
+// compromised credentials in the Password Issues UX.).
 - (instancetype)initWithCredential:
-    (password_manager::CredentialUIEntry)credential NS_DESIGNATED_INITIALIZER;
+                    (password_manager::CredentialUIEntry)credential
+      enableCompromisedDescription:(BOOL)enableCompromisedDescription
+    NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)init NS_UNAVAILABLE;
 
diff --git a/ios/chrome/browser/ui/settings/password/password_issues/password_issue.mm b/ios/chrome/browser/ui/settings/password/password_issues/password_issue.mm
index 79c663d..5a5bda9 100644
--- a/ios/chrome/browser/ui/settings/password/password_issues/password_issue.mm
+++ b/ios/chrome/browser/ui/settings/password/password_issues/password_issue.mm
@@ -8,15 +8,24 @@
 #import "components/password_manager/core/browser/password_ui_utils.h"
 #import "components/password_manager/core/browser/ui/credential_ui_entry.h"
 #import "ios/chrome/browser/net/crurl.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+@interface PasswordIssue () {
+  // Whether the description for compromised credentials should be displayed.
+  BOOL _compromisedDescriptionEnabled;
+}
+@end
+
 @implementation PasswordIssue
 
 - (instancetype)initWithCredential:
-    (password_manager::CredentialUIEntry)credential {
+                    (password_manager::CredentialUIEntry)credential
+      enableCompromisedDescription:(BOOL)enableCompromisedDescription {
   self = [super init];
   if (self) {
     _credential = credential;
@@ -24,8 +33,25 @@
         base::SysUTF8ToNSString(password_manager::GetShownOrigin(credential));
     _username = base::SysUTF16ToNSString(credential.username);
     _URL = [[CrURL alloc] initWithGURL:credential.GetURL()];
+    _compromisedDescriptionEnabled = enableCompromisedDescription;
   }
   return self;
 }
 
+- (NSString*)compromisedDescription {
+  if (_compromisedDescriptionEnabled) {
+    if (_credential.IsLeaked()) {
+      return l10n_util::GetNSString(
+          IDS_IOS_COMPROMISED_PASSWORD_ISSUES_LEAKED_DESCRIPTION);
+    }
+
+    if (_credential.IsPhished()) {
+      return l10n_util::GetNSString(
+          IDS_IOS_COMPROMISED_PASSWORD_ISSUES_PHISHED_DESCRIPTION);
+    }
+  }
+
+  return nil;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/settings/password/password_issues/password_issue_content_item.mm b/ios/chrome/browser/ui/settings/password/password_issues/password_issue_content_item.mm
index c05332f..8add46d 100644
--- a/ios/chrome/browser/ui/settings/password/password_issues/password_issue_content_item.mm
+++ b/ios/chrome/browser/ui/settings/password/password_issues/password_issue_content_item.mm
@@ -18,6 +18,7 @@
   self.title = password.website;
   self.detailText = password.username;
   self.URL = password.URL;
+  self.thirdRowText = password.compromisedDescription;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/settings/password/password_issues/password_issues_mediator.mm b/ios/chrome/browser/ui/settings/password/password_issues/password_issues_mediator.mm
index 9b1adbb..8c6b44e 100644
--- a/ios/chrome/browser/ui/settings/password/password_issues/password_issues_mediator.mm
+++ b/ios/chrome/browser/ui/settings/password/password_issues/password_issues_mediator.mm
@@ -39,13 +39,22 @@
 
 namespace {
 
-// Maps CredentialUIEntry to PasswordIssue and sorts them by website and
-// username.
+// Creates PasswordIssues from CredentialUIEntry to display them in the Password
+// Issues list UI for the given `warning_type`. PasswordIssues are sorted by
+// website and username.
 NSArray<PasswordIssue*>* GetSortedPasswordIssues(
+    WarningType warning_type,
     const std::vector<CredentialUIEntry>& insecure_credentials) {
   NSMutableArray<PasswordIssue*>* passwords = [[NSMutableArray alloc] init];
+
+  BOOL enable_compromised_description =
+      IsPasswordCheckupEnabled() &&
+      warning_type == WarningType::kCompromisedPasswordsWarning;
+
   for (auto credential : insecure_credentials) {
-    [passwords addObject:[[PasswordIssue alloc] initWithCredential:credential]];
+    [passwords addObject:[[PasswordIssue alloc] initWithCredential:credential
+                                      enableCompromisedDescription:
+                                          enable_compromised_description]];
   }
 
   NSSortDescriptor* origin = [[NSSortDescriptor alloc] initWithKey:@"website"
@@ -91,9 +100,11 @@
   [same_password_issues
       enumerateObjectsUsingBlock:^(NSMutableArray<PasswordIssue*>* issues,
                                    NSUInteger index, BOOL* stop) {
-        // TODO(crbug.com/1406540): Add header for each group.
+        NSString* headerText =
+            l10n_util::GetNSStringF(IDS_IOS_REUSED_PASSWORD_ISSUES_GROUP_HEADER,
+                                    base::NumberToString16(issues.count));
         [password_issue_groups
-            addObject:[[PasswordIssueGroup alloc] initWithHeaderText:nil
+            addObject:[[PasswordIssueGroup alloc] initWithHeaderText:headerText
                                                       passwordIssues:issues]];
       }];
 
@@ -111,7 +122,7 @@
 
   // Sort by website and username.
   NSArray<PasswordIssue*>* sorted_issues =
-      GetSortedPasswordIssues(insecure_credentials);
+      GetSortedPasswordIssues(warning_type, insecure_credentials);
 
   // Reused issues are grouped by passwords.
   if (warning_type == WarningType::kReusedPasswordsWarning) {
diff --git a/ios/chrome/browser/ui/settings/password/password_issues/password_issues_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_issues/password_issues_table_view_controller.mm
index 93e4c9f..3d8fd0f 100644
--- a/ios/chrome/browser/ui/settings/password/password_issues/password_issues_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_issues/password_issues_table_view_controller.mm
@@ -6,11 +6,14 @@
 
 #import <UIKit/UIKit.h>
 #import "base/mac/foundation_util.h"
+#import "components/password_manager/core/common/password_manager_features.h"
+#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_item.h"
 #import "ios/chrome/browser/shared/ui/table_view/table_view_favicon_data_source.h"
 #import "ios/chrome/browser/ui/settings/password/password_issues/password_issue_content_item.h"
 #import "ios/chrome/browser/ui/settings/password/password_issues/password_issues_consumer.h"
 #import "ios/chrome/browser/ui/settings/password/password_issues/password_issues_presenter.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/favicon/favicon_view.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ui/base/l10n/l10n_util_mac.h"
@@ -19,15 +22,31 @@
 #error "This file requires ARC support."
 #endif
 
+using password_manager::features::IsPasswordCheckupEnabled;
+
 namespace {
 
+// Vertical spacing between password issue items.
+constexpr CGFloat kVerticalSpacingBetweenItems = 8;
+
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
-  SectionIdentifierContent = kSectionIdentifierEnumZero,
+  SectionIdentifierHeader = kSectionIdentifierEnumZero,
+  SectionIdentifierContent,
+  // Identifier of the section containing the first password issue when Password
+  // Checkup is enabled. Subsequent password issues use incremental section
+  // identifiers, as each issue goes in a separate section. To avoid section
+  // identifiers collisions, `SectionIdentifierFirstPasswordIssue` should be
+  // the last value in `SectionIdentifier` and any new identifiers must be
+  // defined above it.
+  SectionIdentifierFirstPasswordIssue,
+  // Do not add more values here. See comment above.
 };
 
 typedef NS_ENUM(NSInteger, ItemType) {
   ItemTypeHeader = kItemTypeEnumZero,
-  ItemTypePassword,  // This is a repeated item type.
+  ItemTypePassword,        // This is a repeated item type.
+  ItemTypePasswordHeader,  // This is a repeated item type.
+  ItemTypeChangePassword,  // This is a repeated item type.
 };
 
 }  // namespace
@@ -37,10 +56,13 @@
   NSString* _headerText;
   // URL of link in the page header. Nullable.
   CrURL* _headerURL;
+  // Insecure password issues displayed in the tableView.
+  // Reused password issues are displayed in groups of same-password credentials
+  // with a text header on top of the first password issue in the group. All
+  // other types of issues are displayed in the same group without header.
+  NSArray<PasswordIssueGroup*>* _passwordGroups;
 }
 
-@property(nonatomic, strong) NSArray<PasswordIssue*>* passwords;
-
 @end
 
 @implementation PasswordIssuesTableViewController
@@ -66,18 +88,66 @@
 - (void)loadModel {
   [super loadModel];
 
+  if (!IsPasswordCheckupEnabled()) {
+    [self loadModelLegacy];
+    return;
+  }
+
   TableViewModel* model = self.tableViewModel;
-  [model addSectionWithIdentifier:SectionIdentifierContent];
 
   TableViewLinkHeaderFooterItem* headerItem = [self headerItem];
 
   if (headerItem) {
-    // Set header on top of first section.
+    [model addSectionWithIdentifier:SectionIdentifierHeader];
     [model setHeader:headerItem
-        forSectionWithIdentifier:kSectionIdentifierEnumZero];
+        forSectionWithIdentifier:SectionIdentifierHeader];
   }
 
-  for (PasswordIssue* password in self.passwords) {
+  // Add password issues to their own separate sections.
+  __block NSInteger nextPasswordIssueSectionIdentifier =
+      SectionIdentifierFirstPasswordIssue;
+  for (PasswordIssueGroup* issueGroup in _passwordGroups) {
+    [issueGroup.passwordIssues
+        enumerateObjectsUsingBlock:^(PasswordIssue* passwordIssue,
+                                     NSUInteger index, BOOL* stop) {
+          // Create section for next password issue.
+          [model addSectionWithIdentifier:nextPasswordIssueSectionIdentifier];
+
+          // Add header on top of first issue if the issue group has a header.
+          if (index == 0 && issueGroup.headerText) {
+            [model setHeader:[self passwordIssueGroupHeaderItemWithText:
+                                       issueGroup.headerText]
+                forSectionWithIdentifier:nextPasswordIssueSectionIdentifier];
+          }
+
+          // Add password issue.
+          [model addItem:[self passwordIssueItem:passwordIssue]
+              toSectionWithIdentifier:nextPasswordIssueSectionIdentifier];
+
+          // Add change password button below password issue.
+          [model addItem:[self changePasswordItem]
+              toSectionWithIdentifier:nextPasswordIssueSectionIdentifier];
+
+          // Increment section identifier for next password issue.
+          nextPasswordIssueSectionIdentifier++;
+        }];
+  }
+}
+
+// Legacy loadModel logic used when Password Checkup Feature is not enabled.
+- (void)loadModelLegacy {
+  CHECK(!IsPasswordCheckupEnabled());
+
+  TableViewModel* model = self.tableViewModel;
+  [model addSectionWithIdentifier:SectionIdentifierContent];
+  TableViewLinkHeaderFooterItem* headerItem = [self headerItem];
+
+  if (headerItem) {
+    [model setHeader:headerItem
+        forSectionWithIdentifier:SectionIdentifierContent];
+  }
+
+  for (PasswordIssue* password in _passwordGroups.firstObject.passwordIssues) {
     [model addItem:[self passwordIssueItem:password]
         toSectionWithIdentifier:SectionIdentifierContent];
   }
@@ -110,6 +180,27 @@
   return passwordItem;
 }
 
+// Creates a header for displaying on top of a group of password issues.
+- (TableViewLinkHeaderFooterItem*)passwordIssueGroupHeaderItemWithText:
+    (NSString*)headerText {
+  TableViewLinkHeaderFooterItem* groupHeaderItem =
+      [[TableViewLinkHeaderFooterItem alloc]
+          initWithType:ItemTypePasswordHeader];
+  groupHeaderItem.text = headerText;
+  return groupHeaderItem;
+}
+
+// Creates an item acting as a button for changing an insecure password in its
+// corresponding website.
+- (TableViewTextItem*)changePasswordItem {
+  TableViewTextItem* item =
+      [[TableViewTextItem alloc] initWithType:ItemTypeChangePassword];
+  item.text = l10n_util::GetNSString(IDS_IOS_CHANGE_COMPROMISED_PASSWORD);
+  item.textColor = [UIColor colorNamed:kBlueColor];
+  item.accessibilityTraits = UIAccessibilityTraitButton;
+  return item;
+}
+
 #pragma mark - UITableViewDelegate
 
 - (void)tableView:(UITableView*)tableView
@@ -117,10 +208,12 @@
   [super tableView:tableView didSelectRowAtIndexPath:indexPath];
 
   TableViewModel* model = self.tableViewModel;
-  NSInteger itemType = [model itemTypeForIndexPath:indexPath];
+  ItemType itemType =
+      static_cast<ItemType>([model itemTypeForIndexPath:indexPath]);
 
   switch (itemType) {
     case ItemTypeHeader:
+    case ItemTypePasswordHeader:
       break;
     case ItemTypePassword: {
       PasswordIssueContentItem* passwordIssue =
@@ -129,6 +222,9 @@
       [self.presenter presentPasswordIssueDetails:passwordIssue.password];
       break;
     }
+    case ItemTypeChangePassword:
+      // TODO(crbug.com/1406540): Handle change password taps.
+      break;
   }
 
   [tableView deselectRowAtIndexPath:indexPath animated:YES];
@@ -165,6 +261,80 @@
   return view;
 }
 
+- (CGFloat)tableView:(UITableView*)tableView
+    heightForFooterInSection:(NSInteger)section {
+  TableViewModel* model = self.tableViewModel;
+  // Calculate the actual height of the footer view if there's one.
+  if ([model footerForSectionIndex:section]) {
+    return UITableViewAutomaticDimension;
+  }
+
+  NSInteger sectionIdentifier =
+      [model sectionIdentifierForSectionIndex:section];
+  switch (sectionIdentifier) {
+    case SectionIdentifierHeader:
+      // Add an empty footer of 8pt height so added up to the table view header
+      // bottom padding (8pt) and the first section's header (either an empty
+      // 8pt header or an actual header that has a 8pt top padding) achieves the
+      // desired 24pt spacing between the table view header and the next element
+      // below it.
+      return kVerticalSpacingBetweenItems;
+
+    case SectionIdentifierContent:
+      // Vertical spacing between the last item and its container in the legacy
+      // layout.
+      return kVerticalSpacingBetweenItems;
+
+    default:
+      // Handle password issue sections.
+      // All other sections should be handled by now.
+      CHECK_GE(sectionIdentifier, SectionIdentifierFirstPasswordIssue);
+
+      if (section + 1 < model.numberOfSections) {
+        // When the next section doesn't have a header, the desired spacing is
+        // achieved via an empty header. If there's a header, it includes an 8pt
+        // top padding, so we add a 16pt empty footer to achieve the desired
+        // spacing of 24pt.
+        return [model headerForSectionIndex:section + 1]
+                   ? kVerticalSpacingBetweenItems * 2
+                   : 0;
+      }
+
+      // Vertical spacing between the last item and its container.
+      return kVerticalSpacingBetweenItems;
+  }
+}
+
+- (CGFloat)tableView:(UITableView*)tableView
+    heightForHeaderInSection:(NSInteger)section {
+  TableViewModel* model = self.tableViewModel;
+
+  // Calculate the actual height of the header view if there's one.
+  if ([model headerForSectionIndex:section]) {
+    return UITableViewAutomaticDimension;
+  }
+
+  NSInteger sectionIdentifier =
+      [model sectionIdentifierForSectionIndex:section];
+  switch (sectionIdentifier) {
+    case SectionIdentifierHeader:
+      // This section always has a header.
+      NOTREACHED_NORETURN();
+
+    case SectionIdentifierContent:
+      // Keep legacy spacing when no header.
+      return [super tableView:tableView heightForHeaderInSection:section];
+
+    default:
+      // Handle password issue sections.
+      // All other sections should be handled by now.
+      CHECK_GE(sectionIdentifier, SectionIdentifierFirstPasswordIssue);
+      // Add an empty header to achieve the desired spacing to the element
+      // above.
+      return kVerticalSpacingBetweenItems;
+  }
+}
+
 // Asynchronously loads favicon for given index path. The loads are cancelled
 // upon cell reuse automatically.
 - (void)loadFaviconAtIndexPath:(NSIndexPath*)indexPath
@@ -192,10 +362,7 @@
 #pragma mark - PasswordIssuesConsumer
 
 - (void)setPasswordIssues:(NSArray<PasswordIssueGroup*>*)passwordGroups {
-  // TODO(crbug.com/1406540): Replace passwords with passwordGroups to display
-  // all groups.
-  self.passwords =
-      passwordGroups.count == 0 ? @[] : passwordGroups[0].passwordIssues;
+  _passwordGroups = passwordGroups;
   [self reloadData];
 }
 
diff --git a/ios/chrome/browser/ui/settings/password/password_issues/password_issues_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/password_issues/password_issues_table_view_controller_unittest.mm
index 7b2e5a5..0a10f044 100644
--- a/ios/chrome/browser/ui/settings/password/password_issues/password_issues_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_issues/password_issues_table_view_controller_unittest.mm
@@ -76,7 +76,8 @@
     form.signon_realm = "http://www.example.com/";
     form.scheme = password_manager::PasswordForm::Scheme::kHtml;
     PasswordIssue* password_issue = [[PasswordIssue alloc]
-        initWithCredential:password_manager::CredentialUIEntry(form)];
+                  initWithCredential:password_manager::CredentialUIEntry(form)
+        enableCompromisedDescription:NO];
     PasswordIssueGroup* issue_group =
         [[PasswordIssueGroup alloc] initWithHeaderText:nil
                                         passwordIssues:@[ password_issue ]];
diff --git a/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm b/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
index de09eb2..cd2ca7c1 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_coordinator.mm
@@ -211,7 +211,8 @@
   DCHECK(!self.passwordCheckupCoordinator);
   self.passwordCheckupCoordinator = [[PasswordCheckupCoordinator alloc]
       initWithBaseNavigationController:self.baseNavigationController
-                               browser:self.browser];
+                               browser:self.browser
+                          reauthModule:self.reauthModule];
   self.passwordCheckupCoordinator.delegate = self;
   [self.passwordCheckupCoordinator start];
 }
diff --git a/ios/web/find_in_page/README.md b/ios/web/find_in_page/README.md
new file mode 100644
index 0000000..772c614b
--- /dev/null
+++ b/ios/web/find_in_page/README.md
@@ -0,0 +1,2 @@
+This directory contains the implementation of searching for text on a web page.
+The public API is exposed in //ios/web/public/find_in_page.
\ No newline at end of file
diff --git a/ios/web/js_messaging/README.md b/ios/web/js_messaging/README.md
new file mode 100644
index 0000000..c39c5fd
--- /dev/null
+++ b/ios/web/js_messaging/README.md
@@ -0,0 +1,2 @@
+Implementation of the JavaScript API for iOS features. See
+//ios/web/public/js_messaging/README.md for details on using these APIs.
\ No newline at end of file
diff --git a/ios/web/public/find_in_page/README.md b/ios/web/public/find_in_page/README.md
index a539948b..dbfd382 100644
--- a/ios/web/public/find_in_page/README.md
+++ b/ios/web/public/find_in_page/README.md
@@ -1 +1,17 @@
-This directory contains API to search for text on a web page. This directory does not support any UI API except content highlighting.
+# Find in Page
+
+This directory contains API to search for text on a web page. This directory
+does not support any UI API except content highlighting.
+
+## AbstractFindInPageManager
+
+This class exposes the primary Find in Page API.
+
+## JavaScriptFindInPageManager
+
+This class implements the `AbstractFindInPageManager` API using JavaScript.
+
+## FindInPageManager
+
+This class implements the `AbstractFindInPageManager` API using the iOS native
+Find in Page API available starting in iOS 16.
diff --git a/ios/web/webui/README.md b/ios/web/webui/README.md
new file mode 100644
index 0000000..6038178d
--- /dev/null
+++ b/ios/web/webui/README.md
@@ -0,0 +1,12 @@
+This directory contains the iOS specific implementation of WebUI pages. Note
+that while other platforms use WebUI as part of the primary UI, iOS uses web UI
+primarily for "debug" pages. (Pages which render in the web view at `chrome://`
+URLs.) The most visible Web UI surface on iOS is currently "web interstitials"
+which are shared across all platforms including iOS.
+
+WebUI on iOS uses mojo to communicate between the webpage JavaScript and the
+native Web UI page controller code. This communication channel is implemented in
+the MojoFacade class in this directory.
+
+For more details about webui itself, see the webui docs at `//docs/webui*.md`
+and the shared code at `//ui/webui/`
diff --git a/ios/web_view/internal/cwv_user_content_controller.mm b/ios/web_view/internal/cwv_user_content_controller.mm
index b91a043..aa1d2cb 100644
--- a/ios/web_view/internal/cwv_user_content_controller.mm
+++ b/ios/web_view/internal/cwv_user_content_controller.mm
@@ -5,15 +5,38 @@
 #import "ios/web_view/public/cwv_user_content_controller.h"
 #import "ios/web_view/internal/cwv_user_content_controller_internal.h"
 
+#import "base/json/json_writer.h"
+#import "base/mac/foundation_util.h"
+#import "base/strings/sys_string_conversions.h"
 #import "ios/web_view/internal/cwv_web_view_configuration_internal.h"
 #include "ios/web_view/internal/web_view_browser_state.h"
 #import "ios/web_view/internal/web_view_early_page_script_provider.h"
+#import "ios/web_view/internal/web_view_message_handler_java_script_feature.h"
 #import "ios/web_view/public/cwv_user_script.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+namespace {
+
+// Converts base::Value::Dict to NSDictionary.
+NSDictionary* NSDictionaryFromDictValue(const base::Value::Dict& value) {
+  std::string json;
+  const bool success = base::JSONWriter::Write(value, &json);
+  DCHECK(success) << "Failed to convert base::Value to JSON";
+
+  NSData* json_data = [NSData dataWithBytes:json.c_str() length:json.length()];
+  NSDictionary* ns_dictionary = base::mac::ObjCCastStrict<NSDictionary>(
+      [NSJSONSerialization JSONObjectWithData:json_data
+                                      options:kNilOptions
+                                        error:nil]);
+  DCHECK(ns_dictionary) << "Failed to convert JSON to NSDictionary";
+  return ns_dictionary;
+}
+
+}  // namespace
+
 @interface CWVUserContentController ()
 @property(weak, nonatomic) CWVWebViewConfiguration* configuration;
 @end
@@ -63,4 +86,23 @@
       .SetScript(joinedScript);
 }
 
+- (void)addMessageHandler:(void (^)(NSDictionary* payload))handler
+               forCommand:(NSString*)nsCommand {
+  DCHECK(handler);
+  std::string command = base::SysNSStringToUTF8(nsCommand);
+  WebViewMessageHandlerJavaScriptFeature::FromBrowserState(
+      _configuration.browserState)
+      ->RegisterHandler(
+          command, base::BindRepeating(^(const base::Value::Dict& payload) {
+            handler(NSDictionaryFromDictValue(payload));
+          }));
+}
+
+- (void)removeMessageHandlerForCommand:(NSString*)nsCommand {
+  std::string command = base::SysNSStringToUTF8(nsCommand);
+  WebViewMessageHandlerJavaScriptFeature::FromBrowserState(
+      _configuration.browserState)
+      ->UnregisterHandler(command);
+}
+
 @end
diff --git a/ios/web_view/internal/web_view_message_handler_java_script_feature.h b/ios/web_view/internal/web_view_message_handler_java_script_feature.h
index 4a8c9c05..8ee996e 100644
--- a/ios/web_view/internal/web_view_message_handler_java_script_feature.h
+++ b/ios/web_view/internal/web_view_message_handler_java_script_feature.h
@@ -10,9 +10,14 @@
 
 #import "base/functional/callback.h"
 #import "base/no_destructor.h"
+#import "base/supports_user_data.h"
 #import "base/values.h"
 #import "ios/web/public/js_messaging/java_script_feature.h"
 
+namespace web {
+class BrowserState;
+}  // namespace web
+
 // A feature which listens for messages sent from the webpage to the
 // 'CWVWebViewMessage' message handler and routes them to the callback for the
 // associated command. `command` and `payload` are required top level keys. The
@@ -28,10 +33,26 @@
 //  }
 //  window.webkit.messageHandlers['CWVWebViewMessage'].postMessage(message);
 //
-class WebViewMessageHandlerJavaScriptFeature : public web::JavaScriptFeature {
+class WebViewMessageHandlerJavaScriptFeature
+    : public base::SupportsUserData::Data,
+      public web::JavaScriptFeature {
  public:
+  WebViewMessageHandlerJavaScriptFeature();
+  ~WebViewMessageHandlerJavaScriptFeature() override;
+
+  WebViewMessageHandlerJavaScriptFeature(
+      const WebViewMessageHandlerJavaScriptFeature&) = delete;
+  WebViewMessageHandlerJavaScriptFeature& operator=(
+      const WebViewMessageHandlerJavaScriptFeature&) = delete;
+
   static WebViewMessageHandlerJavaScriptFeature* GetInstance();
 
+  // Returns the WebViewMessageHandlerJavaScriptFeature associated with
+  // `browser_state`, creating one if necessary. `browser_state` must not be
+  // null.
+  static WebViewMessageHandlerJavaScriptFeature* FromBrowserState(
+      web::BrowserState* browser_state);
+
   using WebViewMessageHandlerCallback =
       base::RepeatingCallback<void(const base::Value::Dict& payload)>;
   void RegisterHandler(std::string& command,
@@ -46,13 +67,9 @@
   void ScriptMessageReceived(web::WebState* web_state,
                              const web::ScriptMessage& script_message) override;
 
-  WebViewMessageHandlerJavaScriptFeature();
-  ~WebViewMessageHandlerJavaScriptFeature() override;
-
-  WebViewMessageHandlerJavaScriptFeature(
-      const WebViewMessageHandlerJavaScriptFeature&) = delete;
-  WebViewMessageHandlerJavaScriptFeature& operator=(
-      const WebViewMessageHandlerJavaScriptFeature&) = delete;
+  // Notifies any handler registered in `handlers_` for the command specified in
+  // `message_body`.
+  void NotifyHandlers(const base::Value::Dict& message_body);
 
   std::map<std::string, WebViewMessageHandlerCallback> handlers_;
 };
diff --git a/ios/web_view/internal/web_view_message_handler_java_script_feature.mm b/ios/web_view/internal/web_view_message_handler_java_script_feature.mm
index 8c94d22e..db341d20 100644
--- a/ios/web_view/internal/web_view_message_handler_java_script_feature.mm
+++ b/ios/web_view/internal/web_view_message_handler_java_script_feature.mm
@@ -5,6 +5,7 @@
 #import "ios/web_view/internal/web_view_message_handler_java_script_feature.h"
 
 #import "base/logging.h"
+#import "ios/web/public/browser_state.h"
 #import "ios/web/public/js_messaging/script_message.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -13,6 +14,9 @@
 
 namespace {
 
+const char kWebViewMessageHandlerJavaScriptFeatureKeyName[] =
+    "web_view_message_handler_java_script_feature";
+
 const char kScriptName[] = "cwv_messaging";
 
 const char kWebViewMessageHandlerName[] = "CWVWebViewMessage";
@@ -40,6 +44,24 @@
 WebViewMessageHandlerJavaScriptFeature::
     ~WebViewMessageHandlerJavaScriptFeature() = default;
 
+// static
+WebViewMessageHandlerJavaScriptFeature*
+WebViewMessageHandlerJavaScriptFeature::FromBrowserState(
+    web::BrowserState* browser_state) {
+  DCHECK(browser_state);
+
+  WebViewMessageHandlerJavaScriptFeature* feature =
+      static_cast<WebViewMessageHandlerJavaScriptFeature*>(
+          browser_state->GetUserData(
+              kWebViewMessageHandlerJavaScriptFeatureKeyName));
+  if (!feature) {
+    feature = new WebViewMessageHandlerJavaScriptFeature();
+    browser_state->SetUserData(kWebViewMessageHandlerJavaScriptFeatureKeyName,
+                               base::WrapUnique(feature));
+  }
+  return feature;
+}
+
 void WebViewMessageHandlerJavaScriptFeature::RegisterHandler(
     std::string& command,
     WebViewMessageHandlerCallback handler) {
@@ -68,7 +90,22 @@
   }
   base::Value::Dict message_body = std::move(script_message.body()->GetDict());
 
-  std::string* command = message_body.FindString(kScriptMessageCommandKey);
+  // Pass messages from the non-static instances to the static instance during
+  // transition.
+  // TODO(crbug.com/1426917): Remove static instance of feature.
+  WebViewMessageHandlerJavaScriptFeature* static_instance =
+      WebViewMessageHandlerJavaScriptFeature::GetInstance();
+  if (this != static_instance) {
+    static_instance->NotifyHandlers(message_body);
+  }
+
+  NotifyHandlers(message_body);
+}
+
+void WebViewMessageHandlerJavaScriptFeature::NotifyHandlers(
+    const base::Value::Dict& message_body) {
+  const std::string* command =
+      message_body.FindString(kScriptMessageCommandKey);
   if (!command) {
     return;
   }
@@ -77,7 +114,8 @@
     return;
   }
 
-  base::Value::Dict* payload = message_body.FindDict(kScriptMessagePayloadKey);
+  const base::Value::Dict* payload =
+      message_body.FindDict(kScriptMessagePayloadKey);
   if (!payload) {
     return;
   }
diff --git a/ios/web_view/internal/web_view_web_client.mm b/ios/web_view/internal/web_view_web_client.mm
index dd84a526..bcb0ab5 100644
--- a/ios/web_view/internal/web_view_web_client.mm
+++ b/ios/web_view/internal/web_view_web_client.mm
@@ -96,15 +96,16 @@
 
 std::vector<web::JavaScriptFeature*> WebViewWebClient::GetJavaScriptFeatures(
     web::BrowserState* browser_state) const {
-  return {autofill::AutofillJavaScriptFeature::GetInstance(),
-          autofill::FormHandlersJavaScriptFeature::GetInstance(),
-          autofill::SuggestionControllerJavaScriptFeature::GetInstance(),
-          language::LanguageDetectionJavaScriptFeature::GetInstance(),
-          password_manager::PasswordManagerJavaScriptFeature::GetInstance(),
-          security_interstitials::IOSSecurityInterstitialJavaScriptFeature::
-              GetInstance(),
-          translate::TranslateJavaScriptFeature::GetInstance(),
-          WebViewMessageHandlerJavaScriptFeature::GetInstance()};
+  return {
+      autofill::AutofillJavaScriptFeature::GetInstance(),
+      autofill::FormHandlersJavaScriptFeature::GetInstance(),
+      autofill::SuggestionControllerJavaScriptFeature::GetInstance(),
+      language::LanguageDetectionJavaScriptFeature::GetInstance(),
+      password_manager::PasswordManagerJavaScriptFeature::GetInstance(),
+      security_interstitials::IOSSecurityInterstitialJavaScriptFeature::
+          GetInstance(),
+      translate::TranslateJavaScriptFeature::GetInstance(),
+      WebViewMessageHandlerJavaScriptFeature::FromBrowserState(browser_state)};
 }
 
 NSString* WebViewWebClient::GetDocumentStartScriptForMainFrame(
diff --git a/ios/web_view/public/cwv_defines.h b/ios/web_view/public/cwv_defines.h
index afd683e..268d487 100644
--- a/ios/web_view/public/cwv_defines.h
+++ b/ios/web_view/public/cwv_defines.h
@@ -72,4 +72,8 @@
 // Supports -[CWVWebViewConfiguration leakCheckService].
 #define IOS_WEB_VIEW_SUPPORTS_LEAK_CHECK_SERVICE 1
 
+// Supports -[CWVUserContentController addMessageHandler:forCommand:] and
+// -[CWVUserContentController removeMessageHandlerForCommand] APIs.
+#define IOS_WEB_VIEW_SUPPORTS_USER_CONTENT_CONTROLLER_MESSAGE_HANDLERS 1
+
 #endif  // IOS_WEB_VIEW_PUBLIC_CWV_DEFINES_H_
diff --git a/ios/web_view/public/cwv_user_content_controller.h b/ios/web_view/public/cwv_user_content_controller.h
index 11d33a62..9a66b7bf 100644
--- a/ios/web_view/public/cwv_user_content_controller.h
+++ b/ios/web_view/public/cwv_user_content_controller.h
@@ -29,6 +29,33 @@
 // Removes all associated user scripts.
 - (void)removeAllUserScripts;
 
+// Adds a message handler for messages sent from JavaScript for all CWVWebViews
+// associated with a CWVWebViewConfiguration referencing this
+// `userContentController`.
+// `handler` will be called each time a message is sent with the corresponding
+// value of `command`. To send messages from JavaScript, use the WebKit
+// message handler `CWVWebViewMessage` and provide values for the `command` and
+// `payload` keys.
+// `command` must be a string and match the registered handler `command` string
+// `payload` must be a dictionary.
+//
+// Example call from JavaScript:
+//
+//  let message = {
+//    'command': 'myFeatureMessage',
+//    'payload' : {'key1':'value1', 'key2':42}
+//  }
+//  window.webkit.messageHandlers['CWVWebViewMessage'].postMessage(message);
+//
+// NOTE: Only a single `handler` may be registered for a given `command` per
+// user content controller.
+- (void)addMessageHandler:(void (^)(NSDictionary* payload))handler
+               forCommand:(NSString*)command;
+
+// Removes the message handler associated with `command` previously added with
+// `addMessageHandler:forCommand:`.
+- (void)removeMessageHandlerForCommand:(NSString*)nsCommand;
+
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/ios/web_view/public/cwv_web_view.h b/ios/web_view/public/cwv_web_view.h
index 0877d95..62ecf0d 100644
--- a/ios/web_view/public/cwv_web_view.h
+++ b/ios/web_view/public/cwv_web_view.h
@@ -228,7 +228,10 @@
 - (void)evaluateJavaScript:(NSString*)javaScriptString
                 completion:(void (^)(id result, NSError* error))completion;
 
-// Adds a message handler for messages sent from JavaScript.
+// DEPRECATED: Use `CWVUserContentController addMessageHandler:forCommand:`
+// instead.
+// Adds a message handler for messages sent from JavaScript from *any*
+// CWVWebView.
 // `handler` will be called each time a message is sent with the corresponding
 // value of `command`. To send messages from JavaScript, use the WebKit
 // message handler `CWVWebViewMessage` and provide values for the `command` and
@@ -248,6 +251,8 @@
 - (void)addMessageHandler:(void (^)(NSDictionary* payload))handler
                forCommand:(NSString*)command;
 
+// DEPRECATED: Use `CWVUserContentController removeMessageHandlerForCommand:`
+// instead.
 // Removes the message handler associated with `command` previously added with
 // `addMessageHandler:forCommand:`.
 - (void)removeMessageHandlerForCommand:(NSString*)command;
diff --git a/ios/web_view/shell/shell_view_controller.m b/ios/web_view/shell/shell_view_controller.m
index ef6d621..258ea3b 100644
--- a/ios/web_view/shell/shell_view_controller.m
+++ b/ios/web_view/shell/shell_view_controller.m
@@ -287,6 +287,11 @@
   [configuration.autofillDataManager addObserver:self];
   configuration.syncController.delegate = self;
   [configuration.leakCheckService addObserver:self];
+  [configuration.userContentController
+      addMessageHandler:^(NSDictionary* payload) {
+        NSLog(@"message handler payload received =\n%@", payload);
+      }
+             forCommand:@"messageHandlerCommand"];
   self.webView = [self createWebViewWithConfiguration:configuration];
 }
 
@@ -1021,9 +1026,9 @@
 
   [webView
       addMessageHandler:^(NSDictionary* payload) {
-        NSLog(@"message handler payload received =\n%@", payload);
+        NSLog(@"webview message handler payload received =\n%@", payload);
       }
-             forCommand:@"messageHandlerCommand"];
+             forCommand:@"webViewMessageHandlerCommand"];
 
   return webView;
 }
@@ -1033,7 +1038,7 @@
   [_webView removeObserver:self forKeyPath:@"canGoBack"];
   [_webView removeObserver:self forKeyPath:@"canGoForward"];
   [_webView removeObserver:self forKeyPath:@"loading"];
-  [_webView removeMessageHandlerForCommand:@"messageHandlerCommand"];
+  [_webView removeMessageHandlerForCommand:@"webViewMessageHandlerCommand"];
 
   _webView = nil;
 }
@@ -1042,7 +1047,7 @@
   [_webView removeObserver:self forKeyPath:@"canGoBack"];
   [_webView removeObserver:self forKeyPath:@"canGoForward"];
   [_webView removeObserver:self forKeyPath:@"loading"];
-  [_webView removeMessageHandlerForCommand:@"messageHandlerCommand"];
+  [_webView removeMessageHandlerForCommand:@"webViewMessageHandlerCommand"];
 }
 
 - (BOOL)textFieldShouldReturn:(UITextField*)field {
diff --git a/media/cast/sender/audio_sender_unittest.cc b/media/cast/sender/audio_sender_unittest.cc
index bb08d45..96ce25e9 100644
--- a/media/cast/sender/audio_sender_unittest.cc
+++ b/media/cast/sender/audio_sender_unittest.cc
@@ -94,12 +94,16 @@
 
 class AudioSenderTest : public ::testing::Test {
  protected:
-  AudioSenderTest() {
+  AudioSenderTest()
+      : task_runner_(
+            base::MakeRefCounted<FakeSingleThreadTaskRunner>(&testing_clock_)),
+        cast_environment_(base::MakeRefCounted<CastEnvironment>(&testing_clock_,
+                                                                task_runner_,
+                                                                task_runner_,
+                                                                task_runner_)) {
     InitializeMediaLibrary();
     testing_clock_.Advance(base::TimeTicks::Now() - base::TimeTicks());
-    task_runner_ = new FakeSingleThreadTaskRunner(&testing_clock_);
-    cast_environment_ = new CastEnvironment(&testing_clock_, task_runner_,
-                                            task_runner_, task_runner_);
+
     audio_config_.codec = CODEC_AUDIO_OPUS;
     audio_config_.use_hardware_encoder = false;
     audio_config_.rtp_timebase = kDefaultAudioSamplingRate;
@@ -107,10 +111,12 @@
     audio_config_.max_bitrate = kDefaultAudioEncoderBitrate;
     audio_config_.rtp_payload_type = RtpPayloadType::AUDIO_OPUS;
 
-    transport_ = new TestPacketSender();
+    auto sender = std::make_unique<TestPacketSender>();
+    transport_ = sender.get();
     transport_sender_ = std::make_unique<CastTransportImpl>(
         &testing_clock_, base::TimeDelta(), std::make_unique<TransportClient>(),
-        base::WrapUnique(transport_.get()), task_runner_);
+        std::move(sender), task_runner_);
+
     OperationalStatus operational_status = STATUS_UNINITIALIZED;
     audio_sender_ = std::make_unique<AudioSender>(
         cast_environment_, audio_config_,
@@ -123,11 +129,11 @@
   ~AudioSenderTest() override = default;
 
   base::SimpleTestTickClock testing_clock_;
-  raw_ptr<TestPacketSender> transport_;  // Owned by CastTransport.
+  const scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
+  const scoped_refptr<CastEnvironment> cast_environment_;
   std::unique_ptr<CastTransportImpl> transport_sender_;
-  scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
+  raw_ptr<TestPacketSender> transport_;  // Owned by CastTransport.
   std::unique_ptr<AudioSender> audio_sender_;
-  scoped_refptr<CastEnvironment> cast_environment_;
   FrameSenderConfig audio_config_;
 };
 
diff --git a/media/cast/sender/video_sender_unittest.cc b/media/cast/sender/video_sender_unittest.cc
index a4a9a5f9..d4f340e 100644
--- a/media/cast/sender/video_sender_unittest.cc
+++ b/media/cast/sender/video_sender_unittest.cc
@@ -176,10 +176,11 @@
     testing_clock_.Advance(base::TimeTicks::Now() - base::TimeTicks());
     vea_factory_.SetAutoRespond(true);
     last_pixel_value_ = kPixelValue;
-    transport_ = new TestPacketSender();
+    auto sender = std::make_unique<TestPacketSender>();
+    transport_ = sender.get();
     transport_sender_ = std::make_unique<CastTransportImpl>(
         &testing_clock_, base::TimeDelta(), std::make_unique<TransportClient>(),
-        base::WrapUnique(transport_.get()), task_runner_);
+        std::move(sender), task_runner_);
   }
 
   ~VideoSenderTest() override = default;
@@ -248,8 +249,8 @@
   const scoped_refptr<CastEnvironment> cast_environment_;
   OperationalStatus operational_status_;
   FakeVideoEncodeAcceleratorFactory vea_factory_;
-  raw_ptr<TestPacketSender> transport_;  // Owned by CastTransport.
   std::unique_ptr<CastTransportImpl> transport_sender_;
+  raw_ptr<TestPacketSender> transport_;  // Owned by CastTransport.
   std::unique_ptr<PeerVideoSender> video_sender_;
   int last_pixel_value_;
   base::TimeTicks first_frame_timestamp_;
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 0d6d7f93..b686cae 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -627,77 +627,6 @@
   }
 }
 
-# TODO(crbug.com/1006266): consider using |use_chromeos_video_acceleration|.
-if (enable_av1_decoder && (use_v4l2_codec || use_vaapi)) {
-  test("video_decode_accelerator_tests") {
-    testonly = true
-    sources = [ "video_decode_accelerator_tests.cc" ]
-    data = [ "//media/test/data/" ]
-    deps = [
-      ":buildflags",
-      "test:frame_file_writer",
-      "test:frame_validator",
-      "test:helpers",
-      "test:test_helpers",
-      "test:video_player",
-      "test:video_player_test_environment",
-      "//media:test_support",
-      "//testing/gtest",
-    ]
-  }
-
-  test("video_decode_accelerator_perf_tests") {
-    testonly = true
-    sources = [ "video_decode_accelerator_perf_tests.cc" ]
-    data = [ "//media/test/data/" ]
-    deps = [
-      ":buildflags",
-      "test:helpers",
-      "test:video_player",
-      "test:video_player_test_environment",
-      "//media:test_support",
-      "//sandbox/linux:sandbox_services",
-      "//testing/gtest",
-    ]
-  }
-
-  test("video_encode_accelerator_tests") {
-    testonly = true
-    sources = [ "video_encode_accelerator_tests.cc" ]
-    data = [ "//media/test/data/" ]
-    deps = [
-      ":buildflags",
-      ":common",
-      "test:frame_file_writer",
-      "test:frame_validator",
-      "test:helpers",
-      "test:test_helpers",
-      "test:video_encoder",
-      "test:video_encoder_test_environment",
-      "//media:test_support",
-      "//testing/gtest",
-    ]
-  }
-
-  test("video_encode_accelerator_perf_tests") {
-    testonly = true
-    sources = [ "video_encode_accelerator_perf_tests.cc" ]
-    data = [ "//media/test/data/" ]
-    deps = [
-      ":buildflags",
-      ":common",
-      "test:frame_validator",
-      "test:helpers",
-      "test:test_helpers",
-      "test:video_encoder",
-      "test:video_encoder_test_environment",
-      "test:video_encoder_test_environment",
-      "//media:test_support",
-      "//testing/gtest",
-    ]
-  }
-}
-
 if (enable_hevc_parser_and_hw_decoder) {
   fuzzer_test("media_h265_decoder_fuzzer") {
     sources = [ "h265_decoder_fuzzertest.cc" ]
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn
index 6c2eba6c..9beb7f7 100644
--- a/media/gpu/test/BUILD.gn
+++ b/media/gpu/test/BUILD.gn
@@ -5,6 +5,78 @@
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/ui.gni")
 import("//media/gpu/args.gni")
+import("//media/media_options.gni")
+
+# TODO(crbug.com/1006266): consider using |use_chromeos_video_acceleration|.
+if (enable_av1_decoder && (use_v4l2_codec || use_vaapi)) {
+  test("video_decode_accelerator_tests") {
+    testonly = true
+    sources = [ "video_decode_accelerator_tests.cc" ]
+    data = [ "//media/test/data/" ]
+    deps = [
+      ":frame_file_writer",
+      ":frame_validator",
+      ":helpers",
+      ":test_helpers",
+      ":video_player",
+      ":video_player_test_environment",
+      "//media:test_support",
+      "//media/gpu:buildflags",
+      "//testing/gtest",
+    ]
+  }
+
+  test("video_decode_accelerator_perf_tests") {
+    testonly = true
+    sources = [ "video_decode_accelerator_perf_tests.cc" ]
+    data = [ "//media/test/data/" ]
+    deps = [
+      ":helpers",
+      ":video_player",
+      ":video_player_test_environment",
+      "//media:test_support",
+      "//media/gpu:buildflags",
+      "//sandbox/linux:sandbox_services",
+      "//testing/gtest",
+    ]
+  }
+
+  test("video_encode_accelerator_tests") {
+    testonly = true
+    sources = [ "video_encode_accelerator_tests.cc" ]
+    data = [ "//media/test/data/" ]
+    deps = [
+      ":frame_file_writer",
+      ":frame_validator",
+      ":helpers",
+      ":test_helpers",
+      ":video_encoder",
+      ":video_encoder_test_environment",
+      "//media:test_support",
+      "//media/gpu:buildflags",
+      "//media/gpu:common",
+      "//testing/gtest",
+    ]
+  }
+
+  test("video_encode_accelerator_perf_tests") {
+    testonly = true
+    sources = [ "video_encode_accelerator_perf_tests.cc" ]
+    data = [ "//media/test/data/" ]
+    deps = [
+      ":frame_validator",
+      ":helpers",
+      ":test_helpers",
+      ":video_encoder",
+      ":video_encoder_test_environment",
+      ":video_encoder_test_environment",
+      "//media:test_support",
+      "//media/gpu:buildflags",
+      "//media/gpu:common",
+      "//testing/gtest",
+    ]
+  }
+}
 
 source_set("helpers") {
   testonly = true
diff --git a/media/gpu/video_decode_accelerator_perf_tests.cc b/media/gpu/test/video_decode_accelerator_perf_tests.cc
similarity index 100%
rename from media/gpu/video_decode_accelerator_perf_tests.cc
rename to media/gpu/test/video_decode_accelerator_perf_tests.cc
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/test/video_decode_accelerator_tests.cc
similarity index 100%
rename from media/gpu/video_decode_accelerator_tests.cc
rename to media/gpu/test/video_decode_accelerator_tests.cc
diff --git a/media/gpu/video_encode_accelerator_perf_tests.cc b/media/gpu/test/video_encode_accelerator_perf_tests.cc
similarity index 100%
rename from media/gpu/video_encode_accelerator_perf_tests.cc
rename to media/gpu/test/video_encode_accelerator_perf_tests.cc
diff --git a/media/gpu/video_encode_accelerator_tests.cc b/media/gpu/test/video_encode_accelerator_tests.cc
similarity index 100%
rename from media/gpu/video_encode_accelerator_tests.cc
rename to media/gpu/test/video_encode_accelerator_tests.cc
diff --git a/remoting/protocol/webrtc_video_stream.cc b/remoting/protocol/webrtc_video_stream.cc
index ff44c45d..e84ad7c 100644
--- a/remoting/protocol/webrtc_video_stream.cc
+++ b/remoting/protocol/webrtc_video_stream.cc
@@ -302,8 +302,8 @@
       base::BindRepeating(&WebrtcVideoStream::OnSinkAddedOrUpdated,
                           weak_factory_.GetWeakPtr()));
   rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track =
-      peer_connection_factory->CreateVideoTrack(stream_name_,
-                                                video_track_source_.get());
+      peer_connection_factory->CreateVideoTrack(video_track_source_,
+                                                stream_name_);
 
   webrtc::RtpTransceiverInit init;
   init.stream_ids = {stream_name_};
diff --git a/services/network/oblivious_http_request_handler.cc b/services/network/oblivious_http_request_handler.cc
index a9d8bba1..c38af5a0 100644
--- a/services/network/oblivious_http_request_handler.cc
+++ b/services/network/oblivious_http_request_handler.cc
@@ -13,6 +13,7 @@
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
 #include "net/log/net_log_capture_mode.h"
 #include "net/log/net_log_values.h"
 #include "net/log/net_log_with_source.h"
@@ -27,6 +28,7 @@
 #include "services/network/public/mojom/clear_data_filter.mojom.h"
 #include "services/network/public/mojom/oblivious_http_request.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "services/network/trust_tokens/trust_token_request_helper_factory.h"
 
 namespace network {
@@ -43,8 +45,6 @@
     5 * 1024 * 1024;                               // Request size limit is 5MB
 constexpr size_t kMaxContentTypeSize = 256;        // Per RFC6838
 
-const std::array<char, 2> kHeaderForbiddenChars = {'\n', '\r'};
-
 // Class wrapping quiche::ObliviousHttpClient. Should be created once for each
 // request/response pair.
 class StatefulObliviousHttpClient {
@@ -133,6 +133,19 @@
   return bhttp_request.Serialize().value();
 }
 
+scoped_refptr<net::HttpResponseHeaders> GetSyntheticBhttpResponseHeader(
+    const std::vector<quiche::BinaryHttpRequest::Field>& bhttp_headers) {
+  std::string synthetic_headers = "HTTP/1.1 200 Success\r\n";
+  for (const auto& header : bhttp_headers) {
+    if (!net::HttpUtil::IsValidHeaderName(header.name) ||
+        !net::HttpUtil::IsValidHeaderValue(header.value)) {
+      return nullptr;
+    }
+    synthetic_headers += header.name + ": " + header.value + "\r\n";
+  }
+  return net::HttpResponseHeaders::TryToCreate(synthetic_headers);
+}
+
 }  // namespace
 
 class ObliviousHttpRequestHandler::RequestState {
@@ -242,7 +255,8 @@
     mojo::RemoteSetElementId id,
     TrustTokenStatusOrRequestHelper status_or_helper) {
   if (!status_or_helper.ok()) {
-    RespondWithError(id, net::ERR_TRUST_TOKEN_OPERATION_FAILED);
+    RespondWithError(id, net::ERR_TRUST_TOKEN_OPERATION_FAILED,
+                     /*outer_response_error_code=*/absl::nullopt);
     return;
   }
 
@@ -264,7 +278,8 @@
     absl::optional<net::HttpRequestHeaders> headers,
     mojom::TrustTokenOperationStatus status) {
   if (status != mojom::TrustTokenOperationStatus::kOk) {
-    RespondWithError(id, net::ERR_TRUST_TOKEN_OPERATION_FAILED);
+    RespondWithError(id, net::ERR_TRUST_TOKEN_OPERATION_FAILED,
+                     /*outer_response_error_code=*/absl::nullopt);
     return;
   }
   ContinueHandlingRequest(std::move(headers), id);
@@ -329,14 +344,16 @@
   auto maybe_client = StatefulObliviousHttpClient::CreateFromKeyConfig(
       std::move(state->request->key_config));
   if (!maybe_client) {
-    RespondWithError(id, net::ERR_INVALID_ARGUMENT);
+    RespondWithError(id, net::ERR_INVALID_ARGUMENT,
+                     /*outer_response_error_code=*/absl::nullopt);
     return;
   }
   state->ohttp_client = std::move(maybe_client);
   auto maybe_encrypted_blob =
       state->ohttp_client->EncryptRequest(std::move(padded_payload));
   if (!maybe_encrypted_blob) {
-    RespondWithError(id, net::ERR_FAILED);
+    RespondWithError(id, net::ERR_FAILED,
+                     /*outer_response_error_code=*/absl::nullopt);
     return;
   }
 
@@ -363,8 +380,10 @@
       kMaxResponseSize);
 }
 
-void ObliviousHttpRequestHandler::RespondWithError(mojo::RemoteSetElementId id,
-                                                   int error_code) {
+void ObliviousHttpRequestHandler::RespondWithError(
+    mojo::RemoteSetElementId id,
+    int error_code,
+    absl::optional<int> outer_response_error_code) {
   mojom::ObliviousHttpClient* client = clients_.Get(id);
   auto state_iter = client_state_.find(id);
   DCHECK(client);
@@ -372,7 +391,18 @@
   RequestState* state = state_iter->second.get();
   state->net_log.EndEventWithIntParams(
       net::NetLogEventType::OBLIVIOUS_HTTP_REQUEST_END, "status", error_code);
-  client->OnCompleted(absl::nullopt, error_code);
+
+  network::mojom::ObliviousHttpCompletionResultPtr response_result;
+  if (outer_response_error_code) {
+    DCHECK_NE(outer_response_error_code.value(), net::HTTP_OK);
+    response_result = network::mojom::ObliviousHttpCompletionResult::
+        NewOuterResponseErrorCode(outer_response_error_code.value());
+  } else {
+    response_result =
+        network::mojom::ObliviousHttpCompletionResult::NewNetError(error_code);
+  }
+
+  client->OnCompleted(std::move(response_result));
   clients_.Remove(id);
 
   DCHECK_EQ(1u, client_state_.count(id));
@@ -387,14 +417,22 @@
 
   RequestState* state = state_iter->second.get();
   if (!response) {
-    RespondWithError(id, state->loader->NetError());
+    absl::optional<int> outer_response_error_code;
+    if (state->loader->NetError() == net::ERR_HTTP_RESPONSE_CODE_FAILURE &&
+        state->loader->ResponseInfo() &&
+        state->loader->ResponseInfo()->headers) {
+      outer_response_error_code =
+          state->loader->ResponseInfo()->headers->response_code();
+    }
+    RespondWithError(id, state->loader->NetError(), outer_response_error_code);
     return;
   }
 
   auto maybe_payload =
       state->ohttp_client->DecryptResponse(std::move(*response));
   if (!maybe_payload) {
-    RespondWithError(id, net::ERR_FAILED);
+    RespondWithError(id, net::ERR_INVALID_RESPONSE,
+                     /*outer_response_error_code=*/absl::nullopt);
     return;
   }
 
@@ -410,67 +448,70 @@
         return dict;
       });
 
+  // Parse the inner request.
   auto bhttp_response = quiche::BinaryHttpResponse::Create(*maybe_payload);
   if (!bhttp_response.ok()) {
-    RespondWithError(id, net::ERR_FAILED);
+    RespondWithError(id, net::ERR_INVALID_RESPONSE,
+                     /*outer_response_error_code=*/absl::nullopt);
     return;
   }
 
-  // Check that the inner request was successful.
-  int status_code = bhttp_response->status_code();
-  if (status_code / 100 != 2) {
-    RespondWithError(id, net::ERR_HTTP_RESPONSE_CODE_FAILURE);
+  int inner_status_code = bhttp_response->status_code();
+  scoped_refptr<net::HttpResponseHeaders> headers =
+      GetSyntheticBhttpResponseHeader(bhttp_response->GetHeaderFields());
+  // Check that the header is valid.
+  if (!headers) {
+    RespondWithError(id, net::ERR_INVALID_RESPONSE,
+                     /*outer_response_error_code=*/absl::nullopt);
     return;
   }
 
   if (state->trust_token_helper) {
-    std::string synthetic_headers = "HTTP/1.1 200 Success\r\n";
-    for (const auto& header : bhttp_response->GetHeaderFields()) {
-      // Header names and values can not contain embedded newlines. The rest of
-      // the constraints are handled by HttpResponseHeaders::TryToCreate.
-      if (base::ranges::find_first_of(header.name, kHeaderForbiddenChars) !=
-              header.name.end() ||
-          base::ranges::find_first_of(header.value, kHeaderForbiddenChars) !=
-              header.value.end()) {
-        // Invalid header value
-        RespondWithError(id, net::ERR_INVALID_RESPONSE);
-      }
-      synthetic_headers += header.name + ": " + header.value + "\r\n";
-    }
-    scoped_refptr<net::HttpResponseHeaders> headers =
-        net::HttpResponseHeaders::TryToCreate(synthetic_headers);
-    if (!headers) {
-      RespondWithError(id, net::ERR_INVALID_RESPONSE);
-      return;
-    }
     state->trust_token_helper->Finalize(
         *headers,
         base::BindOnce(
             &ObliviousHttpRequestHandler::OnDoneFinalizingTrustTokenOperation,
-            base::Unretained(this), id, std::string(bhttp_response->body())));
+            base::Unretained(this), id, inner_status_code, std::move(headers),
+            std::string(bhttp_response->body())));
     return;
   }
 
-  NotifyComplete(id, std::string(bhttp_response->body()));
+  NotifyComplete(id, inner_status_code, std::move(headers),
+                 std::string(bhttp_response->body()));
 }
 
 void ObliviousHttpRequestHandler::OnDoneFinalizingTrustTokenOperation(
     mojo::RemoteSetElementId id,
+    int inner_response_code,
+    scoped_refptr<net::HttpResponseHeaders> headers,
     std::string body,
     mojom::TrustTokenOperationStatus status) {
   if (status != mojom::TrustTokenOperationStatus::kOk) {
-    RespondWithError(id, net::ERR_TRUST_TOKEN_OPERATION_FAILED);
+    RespondWithError(id, net::ERR_TRUST_TOKEN_OPERATION_FAILED,
+                     /*outer_response_error_code=*/absl::nullopt);
     return;
   }
-  NotifyComplete(id, std::move(body));
+  NotifyComplete(id, inner_response_code, std::move(headers), std::move(body));
 }
 
-void ObliviousHttpRequestHandler::NotifyComplete(mojo::RemoteSetElementId id,
-                                                 std::string body) {
+void ObliviousHttpRequestHandler::NotifyComplete(
+    mojo::RemoteSetElementId id,
+    int inner_response_code,
+    scoped_refptr<net::HttpResponseHeaders> headers,
+    std::string body) {
   mojom::ObliviousHttpClient* client = clients_.Get(id);
   DCHECK(client);
 
-  client->OnCompleted(std::move(body), net::OK);
+  network::mojom::ObliviousHttpResponsePtr response =
+      network::mojom::ObliviousHttpResponse::New();
+  response->response_code = inner_response_code;
+  response->response_body = std::move(body);
+  response->headers = std::move(headers);
+  network::mojom::ObliviousHttpCompletionResultPtr response_result =
+      network::mojom::ObliviousHttpCompletionResult::NewInnerResponse(
+          std::move(response));
+
+  client->OnCompleted(std::move(response_result));
   clients_.Remove(id);
   client_state_.erase(id);
 }
diff --git a/services/network/oblivious_http_request_handler.h b/services/network/oblivious_http_request_handler.h
index 916529c..ee63858 100644
--- a/services/network/oblivious_http_request_handler.h
+++ b/services/network/oblivious_http_request_handler.h
@@ -76,10 +76,12 @@
   void ContinueHandlingRequest(absl::optional<net::HttpRequestHeaders> headers,
                                mojo::RemoteSetElementId id);
 
-  // Calls the completed event with the specified error code on the
-  // corresponding client. The client with the specified id must be in the
-  // `clients_` set and the `client_state_` map.
-  void RespondWithError(mojo::RemoteSetElementId id, int error_code);
+  // Calls the completed event with the specified net error code and HTTP
+  // response error code on the corresponding client. The client with the
+  // specified id must be in the `clients_` set and the `client_state_` map.
+  void RespondWithError(mojo::RemoteSetElementId id,
+                        int error_code,
+                        absl::optional<int> outer_response_error_code);
 
   // Called by the SimpleURLLoader when the outer request has completed.
   // Performs steps 5 and 6 of the OHTTP request procedure above.
@@ -91,12 +93,17 @@
   // result.
   void OnDoneFinalizingTrustTokenOperation(
       mojo::RemoteSetElementId id,
+      int inner_response_code,
+      scoped_refptr<net::HttpResponseHeaders> headers,
       std::string body,
       mojom::TrustTokenOperationStatus status);
 
   // Notifies the client that the request completed successfully with the
-  // provided response body.
-  void NotifyComplete(mojo::RemoteSetElementId id, std::string body);
+  // provided response headers and body.
+  void NotifyComplete(mojo::RemoteSetElementId id,
+                      int inner_response_code,
+                      scoped_refptr<net::HttpResponseHeaders> headers,
+                      std::string body);
 
   // Handles cleaning up when an ObliviousHttpClient disconnects.
   void OnClientDisconnect(mojo::RemoteSetElementId id);
diff --git a/services/network/oblivious_http_request_handler_unittest.cc b/services/network/oblivious_http_request_handler_unittest.cc
index 33260fa5..708b034 100644
--- a/services/network/oblivious_http_request_handler_unittest.cc
+++ b/services/network/oblivious_http_request_handler_unittest.cc
@@ -66,29 +66,81 @@
 
 class TestOhttpClient : public network::mojom::ObliviousHttpClient {
  public:
-  TestOhttpClient(absl::optional<std::string> expected_body,
-                  int expected_status)
-      : expected_body_(std::move(expected_body)),
-        expected_status_(expected_status),
-        receiver_(this) {}
+  enum class ResponseType { kSuccess, kNetError, kOuterResponseErrorCode };
+
+  TestOhttpClient() : receiver_(this) {}
+
+  void SetExpectedNetError(int expected_net_error) {
+    expected_response_type_ = ResponseType::kNetError;
+    expected_net_error_ = expected_net_error;
+  }
+
+  void SetExpectedOuterResponseErrorCode(
+      int expected_outer_response_error_code) {
+    expected_response_type_ = ResponseType::kOuterResponseErrorCode;
+    expected_outer_response_error_code_ = expected_outer_response_error_code;
+  }
+
+  void SetExpectedInnerResponse(
+      int expected_inner_response_code,
+      std::string expected_body,
+      std::multimap<std::string, std::string> expected_headers) {
+    expected_response_type_ = ResponseType::kSuccess;
+    expected_inner_response_code_ = expected_inner_response_code;
+    expected_body_ = expected_body;
+    expected_headers_ = std::move(expected_headers);
+  }
 
   mojo::PendingRemote<network::mojom::ObliviousHttpClient>
   CreatePendingRemote() {
     return receiver_.BindNewPipeAndPassRemote();
   }
 
-  void OnCompleted(const absl::optional<std::string>& response,
-                   int net_error) override {
-    EXPECT_EQ(expected_body_, response);
-    EXPECT_EQ(expected_status_, net_error);
+  void OnCompleted(
+      network::mojom::ObliviousHttpCompletionResultPtr status) override {
+    switch (expected_response_type_) {
+      case ResponseType::kSuccess: {
+        ASSERT_TRUE(status->is_inner_response());
+        EXPECT_EQ(expected_inner_response_code_,
+                  status->get_inner_response()->response_code);
+        EXPECT_EQ(expected_body_, status->get_inner_response()->response_body);
+        // Verify headers.
+        size_t iter = 0;
+        std::string name;
+        std::string value;
+        std::multimap<std::string, std::string> actual_headers;
+        while (status->get_inner_response()->headers->EnumerateHeaderLines(
+            &iter, &name, &value)) {
+          actual_headers.insert(
+              std::pair<std::string, std::string>(name, value));
+        }
+        EXPECT_EQ(expected_headers_, actual_headers);
+        break;
+      }
+      case ResponseType::kNetError: {
+        ASSERT_TRUE(status->is_net_error());
+        EXPECT_EQ(expected_net_error_, status->get_net_error());
+        break;
+      }
+      case ResponseType::kOuterResponseErrorCode: {
+        ASSERT_TRUE(status->is_outer_response_error_code());
+        EXPECT_EQ(expected_outer_response_error_code_,
+                  status->get_outer_response_error_code());
+        break;
+      }
+    }
     run_loop_.Quit();
   }
 
   void WaitForCall() { run_loop_.Run(); }
 
  private:
-  const absl::optional<std::string> expected_body_;
-  const int expected_status_;
+  ResponseType expected_response_type_;
+  int expected_inner_response_code_ = 0;
+  std::string expected_body_;
+  std::multimap<std::string, std::string> expected_headers_;
+  int expected_outer_response_error_code_ = 0;
+  int expected_net_error_ = 0;
   mojo::Receiver<network::mojom::ObliviousHttpClient> receiver_;
   base::RunLoop run_loop_;
 };
@@ -142,6 +194,7 @@
   }
 
   void RespondToPendingRequest(std::string body,
+                               std::multimap<std::string, std::string> headers,
                                GURL relay_url = GURL(kRelayURL),
                                net::HttpStatusCode status = net::HTTP_OK) {
     const network::ResourceRequest* pending_request;
@@ -163,6 +216,9 @@
 
     quiche::BinaryHttpResponse bhttp_response(status);
     bhttp_response.set_body(std::move(body));
+    for (auto kv : headers) {
+      bhttp_response.AddHeaderField({kv.first, kv.second});
+    }
     auto payload = bhttp_response.Serialize();
     ASSERT_TRUE(payload.ok()) << payload.status();
 
@@ -227,22 +283,34 @@
   std::unique_ptr<network::ObliviousHttpRequestHandler> handler =
       CreateHandler();
   {
-    TestOhttpClient client("", net::OK);
+    TestOhttpClient client;
+    client.SetExpectedInnerResponse(
+        /*expected_inner_response_code=*/net::HTTP_OK,
+        /*expected_body=*/"",
+        /*expected_headers=*/{});
     handler->StartRequest(CreateRequest(), client.CreatePendingRemote());
   }
 
   {
-    TestOhttpClient client("", net::OK);
+    TestOhttpClient client;
+    client.SetExpectedInnerResponse(
+        /*expected_inner_response_code=*/net::HTTP_OK,
+        /*expected_body=*/"",
+        /*expected_headers=*/{});
     handler->StartRequest(CreateRequest(), client.CreatePendingRemote());
-    RespondToPendingRequest("");
+    RespondToPendingRequest("", {});
     client.WaitForCall();
   }
   // Empty body
   {
-    TestOhttpClient client("", net::OK);
+    TestOhttpClient client;
+    client.SetExpectedInnerResponse(
+        /*expected_inner_response_code=*/net::HTTP_OK,
+        /*expected_body=*/"",
+        /*expected_headers=*/{});
     handler->StartRequest(CreateRequestWithoutBody(),
                           client.CreatePendingRemote());
-    RespondToPendingRequest("");
+    RespondToPendingRequest("", {});
     client.WaitForCall();
   }
 }
@@ -253,7 +321,8 @@
   {
     mojo::FakeMessageDispatchContext context;
     mojo::test::BadMessageObserver obs;
-    TestOhttpClient client(absl::nullopt, net::ERR_INVALID_URL);
+    TestOhttpClient client;
+    client.SetExpectedNetError(net::ERR_INVALID_URL);
     network::mojom::ObliviousHttpRequestPtr request =
         network::mojom::ObliviousHttpRequest::New();
 
@@ -263,7 +332,8 @@
   {
     mojo::FakeMessageDispatchContext context;
     mojo::test::BadMessageObserver obs;
-    TestOhttpClient client(absl::nullopt, net::ERR_INVALID_URL);
+    TestOhttpClient client;
+    client.SetExpectedNetError(net::ERR_INVALID_URL);
     network::mojom::ObliviousHttpRequestPtr request = CreateRequest();
     request->relay_url = GURL();
 
@@ -273,7 +343,8 @@
   {
     mojo::FakeMessageDispatchContext context;
     mojo::test::BadMessageObserver obs;
-    TestOhttpClient client(absl::nullopt, net::ERR_INVALID_URL);
+    TestOhttpClient client;
+    client.SetExpectedNetError(net::ERR_INVALID_URL);
     network::mojom::ObliviousHttpRequestPtr request = CreateRequest();
     request->resource_url = GURL();
 
@@ -283,7 +354,8 @@
   {
     mojo::FakeMessageDispatchContext context;
     mojo::test::BadMessageObserver obs;
-    TestOhttpClient client(absl::nullopt, net::ERR_INVALID_ARGUMENT);
+    TestOhttpClient client;
+    client.SetExpectedNetError(net::ERR_INVALID_ARGUMENT);
     network::mojom::ObliviousHttpRequestPtr request = CreateRequest();
     request->traffic_annotation = net::MutableNetworkTrafficAnnotationTag();
 
@@ -293,7 +365,8 @@
   {
     mojo::FakeMessageDispatchContext context;
     mojo::test::BadMessageObserver obs;
-    TestOhttpClient client(absl::nullopt, net::ERR_INVALID_ARGUMENT);
+    TestOhttpClient client;
+    client.SetExpectedNetError(net::ERR_INVALID_ARGUMENT);
     network::mojom::ObliviousHttpRequestPtr request = CreateRequest();
     request->method = std::string(17, 'A');
 
@@ -303,7 +376,8 @@
   {
     mojo::FakeMessageDispatchContext context;
     mojo::test::BadMessageObserver obs;
-    TestOhttpClient client(absl::nullopt, net::ERR_INVALID_ARGUMENT);
+    TestOhttpClient client;
+    client.SetExpectedNetError(net::ERR_INVALID_ARGUMENT);
     network::mojom::ObliviousHttpRequestPtr request = CreateRequest();
     request->request_body->content = std::string(5 * 1024 * 1024 + 1, ' ');
 
@@ -313,7 +387,8 @@
   {
     mojo::FakeMessageDispatchContext context;
     mojo::test::BadMessageObserver obs;
-    TestOhttpClient client(absl::nullopt, net::ERR_INVALID_ARGUMENT);
+    TestOhttpClient client;
+    client.SetExpectedNetError(net::ERR_INVALID_ARGUMENT);
     network::mojom::ObliviousHttpRequestPtr request = CreateRequest();
     request->request_body->content_type = std::string(257, ' ');
 
@@ -326,7 +401,12 @@
   std::unique_ptr<network::ObliviousHttpRequestHandler> handler =
       CreateHandler();
   {
-    TestOhttpClient client("response body", net::OK);
+    TestOhttpClient client;
+    client.SetExpectedInnerResponse(
+        /*expected_inner_response_code=*/net::HTTP_OK,
+        /*expected_body=*/"response body",
+        /*expected_headers=*/
+        {{"cache-control", "s-maxage=3600"}, {"content-type", "text/html"}});
 
     handler->StartRequest(CreateRequest(), client.CreatePendingRemote());
     const network::ResourceRequest* pending_request;
@@ -366,7 +446,9 @@
                     {"content-type", "application/testdata"},
                 }));
     EXPECT_EQ(request.body(), "test data");
-    RespondToPendingRequest("response body");
+    RespondToPendingRequest(
+        "response body",
+        {{"cache-control", "s-maxage=3600"}, {"content-type", "text/html"}});
     client.WaitForCall();
   }
 }
@@ -376,7 +458,23 @@
       CreateHandler();
   {
     loader_factory()->AddResponse(kRelayURL, "", net::HTTP_NOT_FOUND);
-    TestOhttpClient client(absl::nullopt, net::ERR_HTTP_RESPONSE_CODE_FAILURE);
+    TestOhttpClient client;
+    client.SetExpectedOuterResponseErrorCode(net::HTTP_NOT_FOUND);
+
+    handler->StartRequest(CreateRequest(), client.CreatePendingRemote());
+    client.WaitForCall();
+  }
+  {
+    loader_factory()->AddResponse(
+        GURL(kRelayURL), network::CreateURLResponseHead(net::HTTP_OK), "",
+        network::URLLoaderCompletionStatus(net::ERR_CONNECTION_RESET),
+        network::TestURLLoaderFactory::Redirects(),
+        network::TestURLLoaderFactory::ResponseProduceFlags::
+            kSendHeadersOnNetworkError);
+    TestOhttpClient client;
+    // The outer HTTP error code should be set only when the net error is
+    // ERR_HTTP_RESPONSE_CODE_FAILURE. Otherwise, log the net error instead.
+    client.SetExpectedNetError(net::ERR_CONNECTION_RESET);
 
     handler->StartRequest(CreateRequest(), client.CreatePendingRemote());
     client.WaitForCall();
@@ -387,10 +485,29 @@
   std::unique_ptr<network::ObliviousHttpRequestHandler> handler =
       CreateHandler();
   {
-    TestOhttpClient client(absl::nullopt, net::ERR_HTTP_RESPONSE_CODE_FAILURE);
+    TestOhttpClient client;
+    client.SetExpectedInnerResponse(
+        /*expected_inner_response_code=*/net::HTTP_NOT_FOUND,
+        /*expected_body=*/"",
+        /*expected_headers=*/
+        {{"cache-control", "s-maxage=60"}});
 
     handler->StartRequest(CreateRequest(), client.CreatePendingRemote());
-    RespondToPendingRequest("", GURL(kRelayURL), net::HTTP_NOT_FOUND);
+    RespondToPendingRequest("", {{"cache-control", "s-maxage=60"}},
+                            GURL(kRelayURL), net::HTTP_NOT_FOUND);
+    client.WaitForCall();
+  }
+  {
+    TestOhttpClient client;
+    client.SetExpectedNetError(net::ERR_INVALID_RESPONSE);
+
+    handler->StartRequest(CreateRequest(), client.CreatePendingRemote());
+    ASSERT_TRUE(loader_factory()->IsPending(kRelayURL));
+    EXPECT_TRUE(loader_factory()->SimulateResponseForPendingRequest(
+        /*url=*/GURL(kRelayURL),
+        /*completion_status=*/network::URLLoaderCompletionStatus(),
+        /*response_head=*/network::CreateURLResponseHead(net::HTTP_OK),
+        /*content=*/"malformed inner response"));
     client.WaitForCall();
   }
 }
@@ -399,19 +516,28 @@
   std::unique_ptr<network::ObliviousHttpRequestHandler> handler =
       CreateHandler();
   {
-    TestOhttpClient client_a("Response a", net::OK);
+    TestOhttpClient client_a;
+    client_a.SetExpectedInnerResponse(
+        /*expected_inner_response_code=*/net::HTTP_OK,
+        /*expected_body=*/"Response a",
+        /*expected_headers=*/{{"cache-control", "s-maxage=60"}});
     network::mojom::ObliviousHttpRequestPtr request_a = CreateRequest();
-    TestOhttpClient client_b("Response b", net::OK);
+    TestOhttpClient client_b;
+    client_b.SetExpectedInnerResponse(
+        /*expected_inner_response_code=*/net::HTTP_OK,
+        /*expected_body=*/"Response b",
+        /*expected_headers=*/{{"cache-control", "s-maxage=600"}});
     network::mojom::ObliviousHttpRequestPtr request_b = CreateRequest();
     request_b->relay_url = GURL("https://another.relay.test");
 
     handler->StartRequest(std::move(request_a), client_a.CreatePendingRemote());
     handler->StartRequest(std::move(request_b), client_b.CreatePendingRemote());
 
-    RespondToPendingRequest("Response b", GURL("https://another.relay.test"));
+    RespondToPendingRequest("Response b", {{"cache-control", "s-maxage=600"}},
+                            GURL("https://another.relay.test"));
     client_b.WaitForCall();
 
-    RespondToPendingRequest("Response a");
+    RespondToPendingRequest("Response a", {{"cache-control", "s-maxage=60"}});
     client_a.WaitForCall();
   }
 }
@@ -420,7 +546,11 @@
   std::unique_ptr<network::ObliviousHttpRequestHandler> handler =
       CreateHandler();
   {
-    TestOhttpClient client("response body", net::OK);
+    TestOhttpClient client;
+    client.SetExpectedInnerResponse(
+        /*expected_inner_response_code=*/net::HTTP_OK,
+        /*expected_body=*/"response body",
+        /*expected_headers=*/{});
     network::mojom::ObliviousHttpRequestPtr request = CreateRequest();
     request->padding_params =
         network::mojom::ObliviousHttpPaddingParameters::New(
@@ -448,7 +578,11 @@
   std::unique_ptr<network::ObliviousHttpRequestHandler> handler =
       CreateHandler();
   {
-    TestOhttpClient client("response body", net::OK);
+    TestOhttpClient client;
+    client.SetExpectedInnerResponse(
+        /*expected_inner_response_code=*/net::HTTP_OK,
+        /*expected_body=*/"response body",
+        /*expected_headers=*/{});
     network::mojom::ObliviousHttpRequestPtr request = CreateRequest();
     request->padding_params =
         network::mojom::ObliviousHttpPaddingParameters::New(
@@ -483,7 +617,11 @@
   double accum_size_squared = 0;
   for (size_t i = 0; i < kNumRuns; i++) {
     {
-      TestOhttpClient client("response body", net::OK);
+      TestOhttpClient client;
+      client.SetExpectedInnerResponse(
+          /*expected_inner_response_code=*/net::HTTP_OK,
+          /*expected_body=*/"response body",
+          /*expected_headers=*/{});
       network::mojom::ObliviousHttpRequestPtr request = CreateRequest();
       request->padding_params =
           network::mojom::ObliviousHttpPaddingParameters::New(
@@ -522,7 +660,11 @@
       CreateHandler();
   base::flat_set<size_t> sizes_seen;
   while (sizes_seen.size() < 2) {
-    TestOhttpClient client("response body", net::OK);
+    TestOhttpClient client;
+    client.SetExpectedInnerResponse(
+        /*expected_inner_response_code=*/net::HTTP_OK,
+        /*expected_body=*/"response body",
+        /*expected_headers=*/{});
     network::mojom::ObliviousHttpRequestPtr request = CreateRequest();
     request->padding_params =
         network::mojom::ObliviousHttpPaddingParameters::New(
diff --git a/services/network/public/mojom/oblivious_http_request.mojom b/services/network/public/mojom/oblivious_http_request.mojom
index f1490cb..24a40c9 100644
--- a/services/network/public/mojom/oblivious_http_request.mojom
+++ b/services/network/public/mojom/oblivious_http_request.mojom
@@ -7,6 +7,7 @@
 import "services/network/public/mojom/isolation_info.mojom";
 import
  "services/network/public/mojom/mutable_network_traffic_annotation_tag.mojom";
+import "services/network/public/mojom/network_param.mojom";
 import "services/network/public/mojom/trust_tokens.mojom";
 import "url/mojom/url.mojom";
 
@@ -17,6 +18,33 @@
   string content_type;
 };
 
+//  The inner response wrapped in binary HTTP from the OHTTP gateway.
+struct ObliviousHttpResponse {
+  // The response code of the inner gateway HTTP response.
+  int32 response_code;
+  // The headers of the inner gateway HTTP response. It should only be used to
+  // get header values. To get the response code, use `response_code` instead.
+  HttpResponseHeaders headers;
+  // The response code of the inner gateway HTTP response. It is empty if
+  // `response_code` is non-200.
+  string response_body;
+};
+
+// The structure used to deliver result to Oblivious HTTP clients.
+union ObliviousHttpCompletionResult {
+  // A general net::Error code in case of failure. If the result is net::OK, one
+  // of the other fields in the union will be populated.
+  int32 net_error;
+  // The response code of the outer relay HTTP response, used to wrap the binary
+  // HTTP (bHTTP) response. This is set iff the result fails because the outer
+  // HTTP response status code is not HTTP_OK (200). The value will never be
+  // 200.
+  int32 outer_response_error_code;
+  // The inner gateway HTTP response. Parsed out from the binary HTTP (bHTTP)
+  // structure.
+  ObliviousHttpResponse inner_response;
+};
+
 struct ObliviousHttpPaddingParameters {
   // Whether to add a random number of bytes following an exponential
   // probability distribution with mean `exponential_mean` extra bytes.
@@ -57,8 +85,6 @@
 
 // Callback interface for Oblivious HTTP Requests.
 interface ObliviousHttpClient {
-  // Called when the OHTTP request completes. `net_error` indicates the
-  // net::Error code for the operation. `response_body` will contain the body
-  // of the response.
-  OnCompleted(string? response_body, int32 net_error);
+  // Called when the OHTTP request completes.
+  OnCompleted(ObliviousHttpCompletionResult response);
 };
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 3b8236e..5f173e9 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -11885,7 +11885,8 @@
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android32_foldable.textpb",
-          "--git-revision=${got_revision}"
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_12l.chrome_public_test_apk.filter"
         ],
         "merge": {
           "args": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 22268f8..946050d3 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -20021,8518 +20021,6 @@
   "ios15-sdk-simulator": {
     "additional_compile_targets": [
       "all"
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "absl_hardening_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "absl_hardening_tests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "absl_hardening_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "absl_hardening_tests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPhone 6s Plus 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPhone 6s Plus 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPhone SE (1st generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPhone SE (1st generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPhone SE (1st generation) 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPhone SE (1st generation) 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "boringssl_crypto_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "boringssl_crypto_tests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "boringssl_crypto_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "boringssl_crypto_tests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "boringssl_ssl_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "boringssl_ssl_tests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "boringssl_ssl_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "boringssl_ssl_tests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPhone 6s Plus 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPhone 6s Plus 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPhone SE (1st generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPhone SE (1st generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPhone SE (1st generation) 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPhone SE (1st generation) 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "crashpad_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "crashpad_tests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "crashpad_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "crashpad_tests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "crypto_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "crypto_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://crypto:crypto_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "crypto_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "crypto_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://crypto:crypto_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPhone 6s Plus 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPhone 6s Plus 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPhone SE (1st generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPhone SE (1st generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPhone SE (1st generation) 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPhone SE (1st generation) 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "google_apis_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "google_apis_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://google_apis:google_apis_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "google_apis_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "google_apis_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://google_apis:google_apis_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_bookmarks_eg2tests_module iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_bookmarks_eg2tests_module iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_bookmarks_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_bookmarks_eg2tests_module iPhone 7 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
-        "variant_id": "iPhone 7 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_bookmarks_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_bookmarks_eg2tests_module iPhone X 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
-        "variant_id": "iPhone X 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_integration_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_integration_eg2tests_module iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_integration_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_integration_eg2tests_module iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_integration_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_integration_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_integration_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_integration_eg2tests_module iPhone 7 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
-        "variant_id": "iPhone 7 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_integration_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_integration_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_integration_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_integration_eg2tests_module iPhone X 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
-        "variant_id": "iPhone X 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_settings_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_settings_eg2tests_module iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_settings_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_settings_eg2tests_module iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_settings_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_settings_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_settings_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_settings_eg2tests_module iPhone 7 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
-        "variant_id": "iPhone 7 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_settings_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_settings_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_settings_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_settings_eg2tests_module iPhone X 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
-        "variant_id": "iPhone X 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_signin_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_signin_eg2tests_module iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_signin_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_signin_eg2tests_module iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_signin_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_signin_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_signin_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_signin_eg2tests_module iPhone 7 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
-        "variant_id": "iPhone 7 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_signin_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_signin_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_signin_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_signin_eg2tests_module iPhone X 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
-        "variant_id": "iPhone X 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_smoke_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_smoke_eg2tests_module iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_smoke_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_smoke_eg2tests_module iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_smoke_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_smoke_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_smoke_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_smoke_eg2tests_module iPhone 7 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
-        "variant_id": "iPhone 7 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_smoke_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_smoke_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_smoke_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_smoke_eg2tests_module iPhone X 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
-        "variant_id": "iPhone X 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_ui_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_ui_eg2tests_module iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_ui_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_ui_eg2tests_module iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_ui_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_ui_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_ui_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_ui_eg2tests_module iPhone 7 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
-        "variant_id": "iPhone 7 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_ui_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_ui_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_ui_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_ui_eg2tests_module iPhone X 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
-        "variant_id": "iPhone X 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPhone 6s Plus 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPhone 6s Plus 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPhone SE (1st generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPhone SE (1st generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPhone SE (1st generation) 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPhone SE (1st generation) 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_web_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_web_eg2tests_module iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_web_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_web_eg2tests_module iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_web_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_web_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_web_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_web_eg2tests_module iPhone 7 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
-        "variant_id": "iPhone 7 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_web_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_web_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_web_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_web_eg2tests_module iPhone X 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
-        "variant_id": "iPhone X 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_components_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/components:ios_components_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_components_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/components:ios_components_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_net_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_net_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/net:ios_net_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_net_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_net_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/net:ios_net_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_remoting_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_remoting_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_showcase_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_showcase_eg2tests_module iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_showcase_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_showcase_eg2tests_module iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_showcase_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_showcase_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_showcase_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_showcase_eg2tests_module iPhone 7 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
-        "variant_id": "iPhone 7 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_showcase_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_showcase_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_showcase_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_showcase_eg2tests_module iPhone X 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
-        "variant_id": "iPhone X 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_testing_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_testing_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/testing:ios_testing_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_testing_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_testing_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/testing:ios_testing_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPhone 6s Plus 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPhone 6s Plus 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPhone SE (1st generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPhone SE (1st generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPhone SE (1st generation) 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPhone SE (1st generation) 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_web_shell_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_shell_eg2tests_module iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_web_shell_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_shell_eg2tests_module iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_web_shell_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_shell_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_web_shell_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_shell_eg2tests_module iPhone 7 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
-        "variant_id": "iPhone 7 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_web_shell_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_shell_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_web_shell_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_shell_eg2tests_module iPhone X 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
-        "variant_id": "iPhone X 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPhone 6s Plus 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPhone 6s Plus 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPhone SE (1st generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPhone SE (1st generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPhone SE (1st generation) 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPhone SE (1st generation) 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPhone 6s Plus 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPhone 6s Plus 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPhone SE (1st generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPhone SE (1st generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPhone SE (1st generation) 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPhone SE (1st generation) 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPhone 6s Plus 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPhone 6s Plus 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPhone SE (1st generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPhone SE (1st generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPhone SE (1st generation) 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPhone SE (1st generation) 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "net_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "net_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://net:net_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "net_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "net_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://net:net_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "services_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "services_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://services:services_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "services_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "services_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://services:services_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPhone 6s Plus 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPhone 6s Plus 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPhone SE (1st generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPhone SE (1st generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPhone SE (1st generation) 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPhone SE (1st generation) 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "sql_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "sql_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://sql:sql_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "sql_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "sql_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://sql:sql_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPad Air 2 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPad Air 2 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPhone 6s Plus 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPhone 6s Plus 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPhone SE (1st generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPhone SE (1st generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone SE (1st generation)",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPhone SE (1st generation) 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPhone SE (1st generation) 15.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "url_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "url_unittests iPhone 6s 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://url:url_unittests/",
-        "variant_id": "iPhone 6s 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s",
-          "--version",
-          "15.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "14c18",
-          "--xctest"
-        ],
-        "isolate_name": "url_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "url_unittests iPhone 6s 15.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-13"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_14c18",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_15_5",
-              "path": "Runtime-ios-15.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://url:url_unittests/",
-        "variant_id": "iPhone 6s 15.5"
-      }
     ]
   },
   "ios16-beta-simulator": {
@@ -34128,6 +25616,114 @@
       {
         "args": [
           "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPad Air 2 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPad Air 2 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPad Pro (12.9-inch) (3rd generation)",
           "--version",
           "16.2",
@@ -34182,6 +25778,222 @@
       {
         "args": [
           "--platform",
+          "iPhone 6s",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPhone 6s 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPhone 6s 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPhone 6s 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPhone 6s 15.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPhone 6s Plus 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPhone 6s Plus 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -34290,6 +26102,114 @@
       {
         "args": [
           "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPhone SE (1st generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPhone SE (1st generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPhone SE (1st generation) 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPhone SE (1st generation) 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone SE (3rd generation)",
           "--version",
           "16.2",
@@ -34722,6 +26642,114 @@
       {
         "args": [
           "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPad Air 2 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPad Air 2 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPad Pro (12.9-inch) (3rd generation)",
           "--version",
           "16.2",
@@ -34776,6 +26804,222 @@
       {
         "args": [
           "--platform",
+          "iPhone 6s",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPhone 6s 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPhone 6s 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPhone 6s 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPhone 6s 15.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPhone 6s Plus 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPhone 6s Plus 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -34884,6 +27128,114 @@
       {
         "args": [
           "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPhone SE (1st generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPhone SE (1st generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPhone SE (1st generation) 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPhone SE (1st generation) 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone SE (3rd generation)",
           "--version",
           "16.2",
@@ -35316,6 +27668,114 @@
       {
         "args": [
           "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPad Air 2 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPad Air 2 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPad Pro (12.9-inch) (3rd generation)",
           "--version",
           "16.2",
@@ -35370,6 +27830,222 @@
       {
         "args": [
           "--platform",
+          "iPhone 6s",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPhone 6s 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPhone 6s 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPhone 6s 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPhone 6s 15.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPhone 6s Plus 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPhone 6s Plus 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -35478,6 +28154,114 @@
       {
         "args": [
           "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPhone SE (1st generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPhone SE (1st generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPhone SE (1st generation) 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPhone SE (1st generation) 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone SE (3rd generation)",
           "--version",
           "16.2",
@@ -36093,6 +28877,63 @@
       {
         "args": [
           "--platform",
+          "iPhone 7",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest",
+          "--xcode-parallelization",
+          "--record-video",
+          "failed_only"
+        ],
+        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_bookmarks_eg2tests_module iPhone 7 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
+        "variant_id": "iPhone 7 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -37365,6 +30206,64 @@
       {
         "args": [
           "--platform",
+          "iPhone 7",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest",
+          "--xcode-parallelization",
+          "--record-video",
+          "failed_only"
+        ],
+        "isolate_name": "ios_chrome_settings_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_settings_eg2tests_module iPhone 7 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
+        "variant_id": "iPhone 7 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -38003,6 +30902,64 @@
       {
         "args": [
           "--platform",
+          "iPhone 7",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest",
+          "--xcode-parallelization",
+          "--record-video",
+          "failed_only"
+        ],
+        "isolate_name": "ios_chrome_signin_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_signin_eg2tests_module iPhone 7 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 6
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
+        "variant_id": "iPhone 7 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -38634,6 +31591,63 @@
       {
         "args": [
           "--platform",
+          "iPhone 7",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest",
+          "--xcode-parallelization",
+          "--record-video",
+          "failed_only"
+        ],
+        "isolate_name": "ios_chrome_smoke_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_smoke_eg2tests_module iPhone 7 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
+        "variant_id": "iPhone 7 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -39268,6 +32282,64 @@
       {
         "args": [
           "--platform",
+          "iPhone 7",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest",
+          "--xcode-parallelization",
+          "--record-video",
+          "failed_only"
+        ],
+        "isolate_name": "ios_chrome_ui_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_ui_eg2tests_module iPhone 7 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 12
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
+        "variant_id": "iPhone 7 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -39554,6 +32626,114 @@
       {
         "args": [
           "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPad Air 2 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPad Air 2 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPad Pro (12.9-inch) (3rd generation)",
           "--version",
           "16.2",
@@ -39608,6 +32788,222 @@
       {
         "args": [
           "--platform",
+          "iPhone 6s",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPhone 6s 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPhone 6s 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPhone 6s 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPhone 6s 15.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPhone 6s Plus 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPhone 6s Plus 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -39716,6 +33112,114 @@
       {
         "args": [
           "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPhone SE (1st generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPhone SE (1st generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPhone SE (1st generation) 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPhone SE (1st generation) 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone SE (3rd generation)",
           "--version",
           "16.2",
@@ -40176,6 +33680,64 @@
       {
         "args": [
           "--platform",
+          "iPhone 7",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest",
+          "--xcode-parallelization",
+          "--record-video",
+          "failed_only"
+        ],
+        "isolate_name": "ios_chrome_web_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_web_eg2tests_module iPhone 7 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
+        "variant_id": "iPhone 7 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -41461,6 +35023,63 @@
       {
         "args": [
           "--platform",
+          "iPhone 7",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest",
+          "--xcode-parallelization",
+          "--record-video",
+          "failed_only"
+        ],
+        "isolate_name": "ios_showcase_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_showcase_eg2tests_module iPhone 7 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
+        "variant_id": "iPhone 7 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -42015,6 +35634,114 @@
       {
         "args": [
           "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPad Air 2 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPad Air 2 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPad Pro (12.9-inch) (3rd generation)",
           "--version",
           "16.2",
@@ -42069,6 +35796,222 @@
       {
         "args": [
           "--platform",
+          "iPhone 6s",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPhone 6s 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPhone 6s 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPhone 6s 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPhone 6s 15.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPhone 6s Plus 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPhone 6s Plus 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -42177,6 +36120,114 @@
       {
         "args": [
           "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPhone SE (1st generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPhone SE (1st generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPhone SE (1st generation) 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPhone SE (1st generation) 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone SE (3rd generation)",
           "--version",
           "16.2",
@@ -42912,6 +36963,114 @@
       {
         "args": [
           "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPad Air 2 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPad Air 2 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPad Pro (12.9-inch) (3rd generation)",
           "--version",
           "16.2",
@@ -42966,6 +37125,222 @@
       {
         "args": [
           "--platform",
+          "iPhone 6s",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPhone 6s 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPhone 6s 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPhone 6s 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPhone 6s 15.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPhone 6s Plus 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPhone 6s Plus 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -43074,6 +37449,114 @@
       {
         "args": [
           "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPhone SE (1st generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPhone SE (1st generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPhone SE (1st generation) 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPhone SE (1st generation) 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone SE (3rd generation)",
           "--version",
           "16.2",
@@ -43182,6 +37665,114 @@
       {
         "args": [
           "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPad Air 2 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPad Air 2 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPad Pro (12.9-inch) (3rd generation)",
           "--version",
           "16.2",
@@ -43236,6 +37827,222 @@
       {
         "args": [
           "--platform",
+          "iPhone 6s",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPhone 6s 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPhone 6s 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPhone 6s 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPhone 6s 15.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPhone 6s Plus 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPhone 6s Plus 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -43344,6 +38151,114 @@
       {
         "args": [
           "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPhone SE (1st generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPhone SE (1st generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPhone SE (1st generation) 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPhone SE (1st generation) 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone SE (3rd generation)",
           "--version",
           "16.2",
@@ -43452,6 +38367,114 @@
       {
         "args": [
           "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPad Air 2 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPad Air 2 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPad Pro (12.9-inch) (3rd generation)",
           "--version",
           "16.2",
@@ -43506,6 +38529,222 @@
       {
         "args": [
           "--platform",
+          "iPhone 6s",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPhone 6s 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPhone 6s 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPhone 6s 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPhone 6s 15.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPhone 6s Plus 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPhone 6s Plus 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -43614,6 +38853,114 @@
       {
         "args": [
           "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPhone SE (1st generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPhone SE (1st generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPhone SE (1st generation) 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPhone SE (1st generation) 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone SE (3rd generation)",
           "--version",
           "16.2",
@@ -44046,6 +39393,114 @@
       {
         "args": [
           "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPad Air 2 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPad Air 2 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPad Pro (12.9-inch) (3rd generation)",
           "--version",
           "16.2",
@@ -44100,6 +39555,222 @@
       {
         "args": [
           "--platform",
+          "iPhone 6s",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPhone 6s 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPhone 6s 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPhone 6s 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPhone 6s 15.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPhone 6s Plus 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPhone 6s Plus 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -44208,6 +39879,114 @@
       {
         "args": [
           "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPhone SE (1st generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPhone SE (1st generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPhone SE (1st generation) 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPhone SE (1st generation) 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone SE (3rd generation)",
           "--version",
           "16.2",
@@ -44478,6 +40257,114 @@
       {
         "args": [
           "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPad Air 2 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPad Air 2 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPad Pro (12.9-inch) (3rd generation)",
           "--version",
           "16.2",
@@ -44532,6 +40419,222 @@
       {
         "args": [
           "--platform",
+          "iPhone 6s",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPhone 6s 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPhone 6s 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPhone 6s 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPhone 6s 15.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPhone 6s Plus 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPhone 6s Plus 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone 8",
           "--version",
           "16.2",
@@ -44640,6 +40743,114 @@
       {
         "args": [
           "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPhone SE (1st generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPhone SE (1st generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone SE (1st generation)",
+          "--version",
+          "15.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "14c18",
+          "--readline-timeout",
+          "600",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPhone SE (1st generation) 15.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-13"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_14c18",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_15_5",
+              "path": "Runtime-ios-15.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPhone SE (1st generation) 15.5"
+      },
+      {
+        "args": [
+          "--platform",
           "iPhone SE (3rd generation)",
           "--version",
           "16.2",
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index ebb1ff2..6e21bf0 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -5298,7 +5298,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5329,7 +5329,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc",
         "merge": {
           "args": [],
@@ -5360,7 +5360,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc",
         "merge": {
           "args": [],
@@ -5395,7 +5395,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --force_high_performance_gpu",
         "merge": {
           "args": [],
@@ -5431,7 +5431,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5467,7 +5467,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5503,7 +5503,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5540,7 +5540,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5571,7 +5571,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc",
         "merge": {
           "args": [],
@@ -5602,7 +5602,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating",
         "merge": {
           "args": [],
@@ -5636,7 +5636,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-gl=angle --use-angle=gles --use-cmd-decoder=passthrough --force_high_performance_gpu",
         "merge": {
           "args": [],
@@ -5670,7 +5670,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
         "merge": {
           "args": [],
@@ -5703,7 +5703,7 @@
         "autotest_name": "chromium_Graphics",
         "bucket": "chromiumos-image-archive",
         "cros_board": "kevin",
-        "cros_img": "kevin-public/R113-15384.0.0",
+        "cros_img": "kevin-public/R114-15397.0.0",
         "extra_browser_args": "--log-level=0 --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
         "merge": {
           "args": [],
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 7916ecb..b03b87fe 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -87,6 +87,7 @@
     "//testing/buildbot/filters/android.pie_arm64_rel.chrome_public_test_apk.filter",
     "//testing/buildbot/filters/android.emulator_11.chrome_public_test_apk.filter",
     "//testing/buildbot/filters/android.emulator_12.chrome_public_test_apk.filter",
+    "//testing/buildbot/filters/android.emulator_12l.chrome_public_test_apk.filter",
     "//testing/buildbot/filters/android.emulator_n.chrome_public_test_apk.filter",
     "//testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter",
   ]
diff --git a/testing/buildbot/filters/android.emulator_12l.chrome_public_test_apk.filter b/testing/buildbot/filters/android.emulator_12l.chrome_public_test_apk.filter
new file mode 100644
index 0000000..50320e04
--- /dev/null
+++ b/testing/buildbot/filters/android.emulator_12l.chrome_public_test_apk.filter
@@ -0,0 +1,37 @@
+# crbug.com/1193745
+-org.chromium.chrome.browser.download.OMADownloadHandlerTest.testClearPendingOMADownloads
+-org.chromium.chrome.browser.download.OMADownloadHandlerTest.testQueryDownloadResult
+
+# crbug.com/1036571
+-org.chromium.chrome.browser.shape_detection.ShapeDetectionTest.testTextDetection
+
+# crbug.com/1036556
+-org.chromium.chrome.browser.usage_stats.TabSuspensionTest.testMediaSuspension
+-org.chromium.chrome.browser.usage_stats.TabSuspensionTest.testTabAddedFromCustomTab
+-org.chromium.chrome.browser.usage_stats.TabSuspensionTest.testMultiWindow
+
+# crbug.com/1195097
+-org.chromium.chrome.browser.permissions.RuntimePermissionTest.testDenyRuntimeDownload
+
+# crbug.com/1225709
+-org.chromium.chrome.browser.contextmenu.ContextMenuTest.testSaveDataUrl
+-org.chromium.chrome.browser.contextmenu.ContextMenuTest.testSaveImage
+-org.chromium.chrome.browser.contextmenu.ContextMenuTest.testSaveVideo
+-org.chromium.components.browser_ui.share.ShareImageFileUtilsTest.testSaveBitmapAndMediaStore
+
+# crbug.com/1225754
+-org.chromium.chrome.browser.offlinepages.OfflinePageBridgeTest.testGetLoadUrlParamsForOpeningMhtmlFileUrl
+
+# crbug.com/1231227
+-org.chromium.chrome.browser.webapps.WebappDisplayModeTest.testFullScreen
+-org.chromium.chrome.browser.webapps.WebappDisplayModeTest.testFullScreenInFullscreen
+
+# crbug.com/1231652
+-org.chromium.chrome.browser.autofill.settings.AutofillProfilesFragmentTest.testKeyboardShownOnDpadCenter
+
+# crbug.com/1297370
+-org.chromium.chrome.browser.multiwindow.MultiWindowIntegrationTest.*
+-org.chromium.chrome.browser.multiwindow.MultiWindowUtilsTest.*
+-org.chromium.chrome.browser.tabmodel.TabModelMergingTest.*
+-org.chromium.chrome.browser.tabmodel.UndoTabModelTest.testOpenRecentlyClosedTabMultiWindow
+-org.chromium.chrome.browser.tabmodel.UndoTabModelTest.testOpenRecentlyClosedTabMultiWindowFallback
diff --git a/testing/buildbot/filters/android.emulator_12l_13.content_browsertests.filter b/testing/buildbot/filters/android.emulator_12l_13.content_browsertests.filter
index 0195205..151cfec 100644
--- a/testing/buildbot/filters/android.emulator_12l_13.content_browsertests.filter
+++ b/testing/buildbot/filters/android.emulator_12l_13.content_browsertests.filter
@@ -21,3 +21,10 @@
 # crbug.com/1420817
 -WebRtcMediaRecorderTest.PausePreventsDataavailableFromBeingFired
 -WebRtcMediaRecorderTest.Start
+
+# crbug.com/1428296
+-All/WebRtcCaptureFromElementBrowserTest.CaptureFromMediaElement/0
+-All/WebRtcCaptureFromElementBrowserTest.CaptureFromMediaElement/2
+
+# crbug.com/1428300
+-OpenCodec/WebRtcMediaRecorderTest.StartAndDataAvailable/2
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 77b7890..cbb5d1d8 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -416,7 +416,7 @@
   },
   'chromeos-kevin-skylab': {
     'cros_board': 'kevin',
-    'cros_img': 'kevin-public/R113-15384.0.0',
+    'cros_img': 'kevin-public/R114-15397.0.0',
     'bucket': 'chromiumos-image-archive',
     'timeout_sec': 10800,
   },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 836605f..e982ac54 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1427,6 +1427,11 @@
         'ci_only': True,
         'experiment_percentage': 100,
       },
+      'android-12l-x64-dbg-tests': {
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_12l.chrome_public_test_apk.filter',
+        ],
+      },
       'android-arm64-proguard-rel': {
         'swarming': {
           'shards': 25,
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 2ab4355..d135a2a 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -6949,49 +6949,6 @@
       },
     },
 
-    'ios15_sdk_simulator_tests': {
-      'ios_common_tests': {
-        'variants': [
-          'SIM_IPHONE_6S_15_5',
-          'SIM_IPHONE_6S_14_5',
-        ],
-      },
-      'ios_eg2_tests': {
-        'mixins': ['xcode_parallelization'],
-        'variants': [
-          'SIM_IPHONE_7_15_5',
-          'SIM_IPAD_AIR_2_15_5',
-          'SIM_IPHONE_X_15_5',
-          'SIM_IPHONE_7_14_5',
-          'SIM_IPAD_AIR_2_14_5',
-          'SIM_IPHONE_X_14_5',
-        ]
-      },
-      'ios_eg2_cq_tests': {
-        'mixins': ['xcode_parallelization'],
-        'variants': [
-          'SIM_IPHONE_7_15_5',
-          'SIM_IPAD_AIR_2_15_5',
-          'SIM_IPHONE_X_15_5',
-          'SIM_IPHONE_7_14_5',
-          'SIM_IPAD_AIR_2_14_5',
-          'SIM_IPHONE_X_14_5',
-        ]
-      },
-      'ios_screen_size_dependent_tests': {
-        'variants': [
-          'SIM_IPHONE_6S_PLUS_14_5',
-          'SIM_IPHONE_6S_14_5',
-          'SIM_IPHONE_SE_1ST_GEN_14_5',
-          'SIM_IPAD_AIR_2_14_5',
-          'SIM_IPHONE_6S_PLUS_15_5',
-          'SIM_IPHONE_6S_15_5',
-          'SIM_IPHONE_SE_1ST_GEN_15_5',
-          'SIM_IPAD_AIR_2_15_5',
-        ],
-      },
-    },
-
     'ios16_beta_simulator_tests': {
       'ios_common_tests': {
         'variants': [
@@ -7066,6 +7023,7 @@
           'SIM_IPAD_AIR_2_15_5',
           'SIM_IPHONE_X_15_5',
           'SIM_IPHONE_7_14_5',
+          'SIM_IPHONE_7_15_5',
           'SIM_IPAD_AIR_2_14_5',
           'SIM_IPHONE_X_14_5',
           'SIM_IPAD_PRO_3RD_GEN_15_5',
@@ -7095,6 +7053,14 @@
           'SIM_IPHONE_SE_3RD_GEN_16_2',
           'SIM_IPAD_AIR_3RD_GEN_16_2',
           'SIM_IPAD_PRO_3RD_GEN_16_2',
+          'SIM_IPHONE_6S_PLUS_14_5',
+          'SIM_IPHONE_6S_14_5',
+          'SIM_IPHONE_SE_1ST_GEN_14_5',
+          'SIM_IPAD_AIR_2_14_5',
+          'SIM_IPHONE_6S_PLUS_15_5',
+          'SIM_IPHONE_6S_15_5',
+          'SIM_IPHONE_SE_1ST_GEN_15_5',
+          'SIM_IPAD_AIR_2_15_5',
         ],
       },
       'ios_swift_interop_xcuitests': {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 2a573c2..d8d1ff92 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3383,9 +3383,6 @@
           'xcode_14_main',
           'xctest',
         ],
-        'test_suites': {
-          'isolated_scripts': 'ios15_sdk_simulator_tests'
-        },
       },
       # ios16-beta-sim compiles with xcode version n-1, but
       # runs testers with xcode n during an xcode roll.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 8a3fbf9a..7ad0648 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -374,7 +374,6 @@
                     "disable_features": [
                         "AndroidScrollOptimizations",
                         "CacheSiteIsolationMemoryThreshold",
-                        "NewSigninRequestHeaderCheckOrder",
                         "UseGetrandomForRandBytes"
                     ]
                 }
@@ -4789,6 +4788,24 @@
             ]
         }
     ],
+    "EndOfLifeIncentive": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "EnabledOffer_20230324",
+                    "params": {
+                        "incentive_type": "offer"
+                    },
+                    "enable_features": [
+                        "EolIncentive"
+                    ]
+                }
+            ]
+        }
+    ],
     "EnterpriseReportingConnectorExtensionEvents": [
         {
             "platforms": [
diff --git a/third_party/blink/public/web/web_ax_context.h b/third_party/blink/public/web/web_ax_context.h
index b74820da..78f8f635 100644
--- a/third_party/blink/public/web/web_ax_context.h
+++ b/third_party/blink/public/web/web_ax_context.h
@@ -70,6 +70,10 @@
                                       bool& had_load_complete_messages,
                                       bool& need_to_send_location_changes);
 
+  // Returns a vector of the images found in |updates|.
+  void GetImagesToAnnotate(ui::AXTreeUpdate& updates,
+                           std::vector<ui::AXNodeData*>&);
+
   // Clears out the list of dirty AXObjects and of pending events.
   void ClearDirtyObjectsAndPendingEvents();
 
diff --git a/third_party/blink/renderer/core/accessibility/ax_object_cache.h b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
index ada944f..52484cc3 100644
--- a/third_party/blink/renderer/core/accessibility/ax_object_cache.h
+++ b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
@@ -245,6 +245,10 @@
       bool& had_load_complete_messages,
       bool& need_to_send_location_changes) = 0;
 
+  // Returns a vector of the images found in |updates|.
+  virtual void GetImagesToAnnotate(ui::AXTreeUpdate& updates,
+                                   std::vector<ui::AXNodeData*>&) = 0;
+
   virtual void ClearDirtyObjectsAndPendingEvents() = 0;
 
   // Note that any pending event also causes its corresponding object to
diff --git a/third_party/blink/renderer/core/animation/interpolable_grid_track_list.cc b/third_party/blink/renderer/core/animation/interpolable_grid_track_list.cc
index d5effbf..d7255b8 100644
--- a/third_party/blink/renderer/core/animation/interpolable_grid_track_list.cc
+++ b/third_party/blink/renderer/core/animation/interpolable_grid_track_list.cc
@@ -35,7 +35,8 @@
 
     const NGGridTrackRepeater repeater(
         track_list.RepeatIndex(i), track_list.RepeatSize(i),
-        track_list.RepeatCount(i, 0), track_list.RepeatType(i));
+        track_list.RepeatCount(i, 0), track_list.LineNameIndicesCount(i),
+        track_list.RepeatType(i));
     std::unique_ptr<InterpolableGridTrackRepeater> result =
         InterpolableGridTrackRepeater::Create(repeater, repeater_track_sizes,
                                               zoom);
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 18690c3..ea9f5b1 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -1411,22 +1411,21 @@
   return nullptr;
 }
 
-enum GridTrackListSerializationType {
-  kForGridElements,
-  kForNonGridElements,
-  kForRepeatNonGridElements,
-};
-
+enum class NamedLinesType { kNamedLines, kAutoRepeatNamedLines };
 class OrderedNamedLinesCollector {
   STACK_ALLOCATED();
 
  public:
   OrderedNamedLinesCollector(
       const OrderedNamedGridLines& ordered_named_grid_lines,
-      const OrderedNamedGridLines& ordered_named_auto_repeat_grid_lines)
+      const OrderedNamedGridLines& ordered_named_auto_repeat_grid_lines,
+      bool is_subgridded_track,
+      bool is_layout_grid)
       : ordered_named_grid_lines_(ordered_named_grid_lines),
         ordered_named_auto_repeat_grid_lines_(
-            ordered_named_auto_repeat_grid_lines) {}
+            ordered_named_auto_repeat_grid_lines),
+        is_subgridded_axis_(is_subgridded_track),
+        is_layout_grid_(is_layout_grid) {}
   OrderedNamedLinesCollector(const OrderedNamedLinesCollector&) = delete;
   OrderedNamedLinesCollector& operator=(const OrderedNamedLinesCollector&) =
       delete;
@@ -1439,55 +1438,52 @@
   bool IsSubgriddedAxis() const { return is_subgridded_axis_; }
   wtf_size_t InsertionPoint() const { return insertion_point_; }
   bool HasAutoRepeatNamedLinesSpecified() const {
-    return ordered_named_auto_repeat_grid_lines_.size() > 0;
+    return AutoRepeatNamedLinesCount() > 0;
+  }
+  wtf_size_t AutoRepeatNamedLinesCount() const {
+    return ordered_named_auto_repeat_grid_lines_.size();
   }
   // A collapsed auto repeat track is a specified auto-repeat track that was
   // clamped to zero repeats. This can only happen for subgrids, as
-  // standalone grids guarantee a minimum of 1 repeat.
+  // standalone grids guarantee a minimum of 1 repeat. This also requires that
+  // auto repetitions were computed, which only happens for layout grids.
   bool HasCollapsedAutoRepeatNamedLines() const {
-    return IsSubgriddedAxis() && HasAutoRepeatNamedLinesSpecified() &&
-           auto_repeat_total_tracks_ == 0;
+    return is_layout_grid_ && IsSubgriddedAxis() &&
+           HasAutoRepeatNamedLinesSpecified() && auto_repeat_total_tracks_ == 0;
   }
-  virtual void CollectLineNamesForIndex(
-      cssvalue::CSSBracketedValueList&,
-      wtf_size_t index,
-      GridTrackListSerializationType named_line_type = kForGridElements) const;
+  virtual void CollectLineNamesForIndex(cssvalue::CSSBracketedValueList&,
+                                        wtf_size_t index,
+                                        NamedLinesType type,
+                                        bool is_in_repeat) const;
 
  protected:
-  enum NamedLinesType { kNamedLines, kAutoRepeatNamedLines };
-  void AppendLines(
-      cssvalue::CSSBracketedValueList&,
-      wtf_size_t index,
-      NamedLinesType,
-      GridTrackListSerializationType named_line_type = kForGridElements) const;
+  void AppendLines(cssvalue::CSSBracketedValueList&,
+                   wtf_size_t index,
+                   NamedLinesType,
+                   bool is_in_repeat = false) const;
 
   const OrderedNamedGridLines& ordered_named_grid_lines_;
   const OrderedNamedGridLines& ordered_named_auto_repeat_grid_lines_;
+
   // The auto-repeat index.
   wtf_size_t insertion_point_{0};
+
   // The total number of auto-repeat tracks, factoring in the number of
   // repetitions (e.g. `repeat(auto-fit, [a][b])` with `auto-fit` calculated at
   // 3 repetitions would be 6).
   wtf_size_t auto_repeat_total_tracks_{0};
+
   // The size of one auto repeat track (e.g. `repeat(auto-fit, [a][b])` has an
   // auto repeat track list length of 2, regardless of the number of repetitions
   // computed for `auto-fit`).
   wtf_size_t auto_repeat_track_list_length_{0};
-  bool is_subgridded_axis_{false};
-};
 
-class OrderedNamedLinesCollectorInsideAutoRepeat
-    : public OrderedNamedLinesCollector {
- public:
-  OrderedNamedLinesCollectorInsideAutoRepeat(
-      const OrderedNamedGridLines& ordered_named_grid_lines,
-      const OrderedNamedGridLines& ordered_named_auto_repeat_grid_lines)
-      : OrderedNamedLinesCollector(ordered_named_grid_lines,
-                                   ordered_named_auto_repeat_grid_lines) {}
-  void CollectLineNamesForIndex(cssvalue::CSSBracketedValueList&,
-                                wtf_size_t index,
-                                GridTrackListSerializationType named_line_type =
-                                    kForGridElements) const override;
+  // Whether the track definition has `subgrid` specified.
+  bool is_subgridded_axis_{false};
+
+  // Whether the element associated with the track collection performs grid
+  // layout.
+  bool is_layout_grid_{false};
 };
 
 class OrderedNamedLinesCollectorInGridLayout
@@ -1499,48 +1495,45 @@
       wtf_size_t insertion_point,
       wtf_size_t auto_repeat_total_tracks,
       wtf_size_t auto_repeat_track_list_length,
-      bool is_subgridded_axis = false)
+      bool is_subgridded_track)
       : OrderedNamedLinesCollector(ordered_named_grid_lines,
-                                   ordered_named_auto_repeat_grid_lines) {
+                                   ordered_named_auto_repeat_grid_lines,
+                                   is_subgridded_track,
+                                   /* is_layout_grid */ true) {
     insertion_point_ = insertion_point;
-    is_subgridded_axis_ = is_subgridded_axis;
     auto_repeat_total_tracks_ = auto_repeat_total_tracks;
     auto_repeat_track_list_length_ = auto_repeat_track_list_length;
   }
   void CollectLineNamesForIndex(cssvalue::CSSBracketedValueList&,
                                 wtf_size_t index,
-                                GridTrackListSerializationType named_line_type =
-                                    kForGridElements) const override;
+                                NamedLinesType type,
+                                bool is_in_repeat) const override;
 };
 
 void OrderedNamedLinesCollector::AppendLines(
     cssvalue::CSSBracketedValueList& line_names_value,
     wtf_size_t index,
     NamedLinesType type,
-    GridTrackListSerializationType named_line_type) const {
-  auto iter = type == kNamedLines
-                  ? ordered_named_grid_lines_.find(index)
-                  : ordered_named_auto_repeat_grid_lines_.find(index);
-  auto end_iter = type == kNamedLines
-                      ? ordered_named_grid_lines_.end()
-                      : ordered_named_auto_repeat_grid_lines_.end();
+    bool is_in_repeat) const {
+  const bool is_auto = type == NamedLinesType::kAutoRepeatNamedLines;
+  auto iter = is_auto ? ordered_named_auto_repeat_grid_lines_.find(index)
+                      : ordered_named_grid_lines_.find(index);
+  auto end_iter = is_auto ? ordered_named_auto_repeat_grid_lines_.end()
+                          : ordered_named_grid_lines_.end();
   if (iter == end_iter) {
     return;
   }
 
   for (auto named_grid_line : iter->value) {
-    // A line name is appended when:
-    // 1. The layout object is the grid.
-    // 2. The layout object is not the grid and the named line isn't part of an
-    // integer repeat.
-    // 3. The layout object is not the grid, the named line is part of the
-    // repeat and is the first time it appears in |ordered_named_grid_lines_|.
-    const bool is_first_repeat =
-        named_grid_line.is_in_repeat && named_grid_line.is_first_repeat;
-    if (named_line_type == kForGridElements ||
-        (named_line_type == kForNonGridElements &&
-         !named_grid_line.is_in_repeat) ||
-        (named_line_type == kForRepeatNonGridElements && is_first_repeat)) {
+    // For layout grids, insert all values. For non-layout grids, in order to
+    // round-trip repeaters, we need to prevent inserting certain line names.
+    // In particular, don't insert lines from repeaters if we're not in a
+    // repeater, and only add the first repeat.
+    const bool is_not_in_repeat =
+        !is_in_repeat && !named_grid_line.is_in_repeat;
+    const bool is_valid_repeat_line =
+        is_in_repeat && named_grid_line.is_first_repeat;
+    if (is_layout_grid_ || is_not_in_repeat || is_valid_repeat_line) {
       line_names_value.Append(*MakeGarbageCollected<CSSCustomIdentValue>(
           AtomicString(named_grid_line.line_name)));
     }
@@ -1550,29 +1543,23 @@
 void OrderedNamedLinesCollector::CollectLineNamesForIndex(
     cssvalue::CSSBracketedValueList& line_names_value,
     wtf_size_t i,
-    GridTrackListSerializationType named_line_type) const {
+    NamedLinesType type,
+    bool is_in_repeat) const {
   DCHECK(IsSubgriddedAxis() || !IsEmpty());
-  AppendLines(line_names_value, i, kNamedLines, named_line_type);
-}
-
-void OrderedNamedLinesCollectorInsideAutoRepeat::CollectLineNamesForIndex(
-    cssvalue::CSSBracketedValueList& line_names_value,
-    wtf_size_t i,
-    GridTrackListSerializationType named_line_type) const {
-  DCHECK(IsSubgriddedAxis() || !IsEmpty());
-  AppendLines(line_names_value, i, kAutoRepeatNamedLines);
+  AppendLines(line_names_value, i, type, is_in_repeat);
 }
 
 void OrderedNamedLinesCollectorInGridLayout::CollectLineNamesForIndex(
     cssvalue::CSSBracketedValueList& line_names_value,
     wtf_size_t i,
-    GridTrackListSerializationType named_line_type) const {
+    NamedLinesType type,
+    bool is_in_repeat) const {
   DCHECK(IsSubgriddedAxis() || !IsEmpty());
 
   // Handle lines before the auto repeat insertion point. If we don't have any
   // auto repeat tracks, we can skip all of the auto repeat logic below.
   if (auto_repeat_total_tracks_ == 0LU || i < insertion_point_) {
-    AppendLines(line_names_value, i, kNamedLines);
+    AppendLines(line_names_value, i, NamedLinesType::kNamedLines);
     return;
   }
 
@@ -1581,22 +1568,23 @@
   // Handle tracks after the auto repeaters.
   if (i > insertion_point_ + auto_repeat_total_tracks_) {
     AppendLines(line_names_value, i - (auto_repeat_total_tracks_ - 1),
-                kNamedLines);
+                NamedLinesType::kNamedLines);
     return;
   }
 
   // Handle the auto repeat track at the insertion point.
   if (i == insertion_point_) {
-    AppendLines(line_names_value, i, kNamedLines);
-    AppendLines(line_names_value, 0, kAutoRepeatNamedLines);
+    AppendLines(line_names_value, i, NamedLinesType::kNamedLines);
+    AppendLines(line_names_value, 0, NamedLinesType::kAutoRepeatNamedLines);
     return;
   }
 
   // Handle the final auto repeat track.
   if (i == insertion_point_ + auto_repeat_total_tracks_) {
     AppendLines(line_names_value, auto_repeat_track_list_length_,
-                kAutoRepeatNamedLines);
-    AppendLines(line_names_value, insertion_point_ + 1, kNamedLines);
+                NamedLinesType::kAutoRepeatNamedLines);
+    AppendLines(line_names_value, insertion_point_ + 1,
+                NamedLinesType::kNamedLines);
     return;
   }
 
@@ -1605,17 +1593,17 @@
       (i - insertion_point_) % auto_repeat_track_list_length_;
   if (!auto_repeat_index_in_first_repetition && i > insertion_point_) {
     AppendLines(line_names_value, auto_repeat_track_list_length_,
-                kAutoRepeatNamedLines);
+                NamedLinesType::kAutoRepeatNamedLines);
   }
   AppendLines(line_names_value, auto_repeat_index_in_first_repetition,
-              kAutoRepeatNamedLines);
+              NamedLinesType::kAutoRepeatNamedLines);
 }
 
-void AddValuesForNamedGridLinesAtIndex(
-    OrderedNamedLinesCollector& collector,
-    wtf_size_t i,
-    CSSValueList& list,
-    GridTrackListSerializationType named_line_type = kForGridElements) {
+void AddValuesForNamedGridLinesAtIndex(OrderedNamedLinesCollector& collector,
+                                       wtf_size_t i,
+                                       CSSValueList& list,
+                                       NamedLinesType type,
+                                       bool is_in_repeat = false) {
   if (collector.IsSubgriddedAxis()) {
     // Skip collapsed lines at the auto repeat insertion point.
     if (i == collector.InsertionPoint() &&
@@ -1627,7 +1615,7 @@
   }
 
   auto* line_names = MakeGarbageCollected<cssvalue::CSSBracketedValueList>();
-  collector.CollectLineNamesForIndex(*line_names, i, named_line_type);
+  collector.CollectLineNamesForIndex(*line_names, i, type, is_in_repeat);
 
   // Subgridded track listings include empty lines per
   // https://www.w3.org/TR/css-grid-2/#resolved-track-list-subgrid.
@@ -1663,11 +1651,10 @@
   return list;
 }
 
-template <typename T, typename F>
 void PopulateGridTrackList(CSSValueList* list,
                            OrderedNamedLinesCollector& collector,
-                           const Vector<T, 1>& tracks,
-                           F GetTrackSize,
+                           const Vector<LayoutUnit, 1>& tracks,
+                           const ComputedStyle& style,
                            wtf_size_t start,
                            wtf_size_t end,
                            int offset) {
@@ -1683,19 +1670,198 @@
   }
   for (wtf_size_t i = start; i < end; ++i) {
     if (offset >= 0 || i >= static_cast<wtf_size_t>(-offset)) {
-      AddValuesForNamedGridLinesAtIndex(collector, i + offset, *list);
+      AddValuesForNamedGridLinesAtIndex(collector, i + offset, *list,
+                                        NamedLinesType::kNamedLines);
     }
     // Subgrids do not include sizes in the track listing.
     if (!collector.IsSubgriddedAxis()) {
       DCHECK_LE(i, tracks.size());
-      list->Append(*GetTrackSize(tracks[i]));
+      list->Append(*ZoomAdjustedPixelValue(tracks[i], style));
     }
   }
   // Subgrid track names are always relative to offset 0, so they can ignore the
   // tracks after the offset.
   if (!collector.IsSubgriddedAxis() &&
       (offset >= 0 || end >= static_cast<wtf_size_t>(-offset))) {
-    AddValuesForNamedGridLinesAtIndex(collector, end + offset, *list);
+    AddValuesForNamedGridLinesAtIndex(collector, end + offset, *list,
+                                      NamedLinesType::kNamedLines);
+  }
+}
+
+void PopulateNonRepeater(CSSValueList* list,
+                         OrderedNamedLinesCollector& collector,
+                         const blink::NGGridTrackList& track_list,
+                         wtf_size_t repeater_index,
+                         wtf_size_t track_index,
+                         const ComputedStyle& style) {
+  DCHECK_EQ(track_list.RepeatType(repeater_index),
+            NGGridTrackRepeater::RepeatType::kNoRepeat);
+
+  AddValuesForNamedGridLinesAtIndex(collector, track_index, *list,
+                                    NamedLinesType::kNamedLines);
+  // Subgrid definitions do not include track sizes.
+  if (!track_list.IsSubgriddedAxis()) {
+    list->Append(*ComputedStyleUtils::SpecifiedValueForGridTrackSize(
+        track_list.RepeatTrackSize(repeater_index, 0), style));
+  }
+}
+
+void PopulateAutoRepeater(CSSValueList* list,
+                          OrderedNamedLinesCollector& collector,
+                          const blink::NGGridTrackList& track_list,
+                          wtf_size_t repeater_index,
+                          const ComputedStyle& style) {
+  blink::NGGridTrackRepeater::RepeatType repeat_type =
+      track_list.RepeatType(repeater_index);
+  DCHECK(repeat_type == NGGridTrackRepeater::RepeatType::kAutoFill ||
+         repeat_type == NGGridTrackRepeater::RepeatType::kAutoFit);
+
+  const bool is_subgrid = track_list.IsSubgriddedAxis();
+  CSSValueList* repeated_values;
+  wtf_size_t repeat_size = is_subgrid
+                               ? track_list.LineNameIndicesCount(repeater_index)
+                               : track_list.RepeatSize(repeater_index);
+
+  repeated_values = MakeGarbageCollected<cssvalue::CSSGridAutoRepeatValue>(
+      repeat_type == NGGridTrackRepeater::RepeatType::kAutoFill
+          ? CSSValueID::kAutoFill
+          : CSSValueID::kAutoFit);
+
+  // Unlike integer repeats, line names for auto repeats start at index 0 and go
+  // to `repeat_size`. This is because auto repeat named lines are in their own
+  // line name collection, while line names for integer repeats are expanded and
+  // interspersed with non-repeaters in the track list.
+  for (wtf_size_t i = 0; i < repeat_size; ++i) {
+    AddValuesForNamedGridLinesAtIndex(collector, i, *repeated_values,
+                                      NamedLinesType::kAutoRepeatNamedLines);
+
+    // Subgrids do not support track sizes.
+    if (!is_subgrid) {
+      const GridTrackSize& track_size =
+          track_list.RepeatTrackSize(repeater_index, i);
+      repeated_values->Append(
+          *ComputedStyleUtils::SpecifiedValueForGridTrackSize(track_size,
+                                                              style));
+    }
+  }
+
+  // Add any additional auto repeat line names after size definitions.
+  for (wtf_size_t i = repeat_size; i < collector.AutoRepeatNamedLinesCount();
+       ++i) {
+    AddValuesForNamedGridLinesAtIndex(collector, i, *repeated_values,
+                                      NamedLinesType::kAutoRepeatNamedLines);
+  }
+  // Subgrids allow for empty line definitions.
+  if (is_subgrid && repeat_size == 0) {
+    repeated_values->Append(
+        *MakeGarbageCollected<cssvalue::CSSBracketedValueList>());
+  }
+
+  list->Append(*repeated_values);
+}
+
+// Returns the number of tracks populated after expanding repetitions.
+wtf_size_t PopulateIntegerRepeater(CSSValueList* list,
+                                   OrderedNamedLinesCollector& collector,
+                                   const blink::NGGridTrackList& track_list,
+                                   wtf_size_t repeater_index,
+                                   wtf_size_t track_index,
+                                   const ComputedStyle& style) {
+  const bool is_subgrid = track_list.IsSubgriddedAxis();
+  CSSValueList* repeated_values;
+  wtf_size_t number_of_repetitions = track_list.RepeatCount(repeater_index, 0);
+  wtf_size_t repeat_size = is_subgrid
+                               ? track_list.LineNameIndicesCount(repeater_index)
+                               : track_list.RepeatSize(repeater_index);
+
+  repeated_values = MakeGarbageCollected<cssvalue::CSSGridIntegerRepeatValue>(
+      number_of_repetitions);
+
+  // Line names for integer repeats get expanded and interspersed with
+  // non-repeaters in the track list.
+  for (wtf_size_t i = 0; i < repeat_size; ++i) {
+    AddValuesForNamedGridLinesAtIndex(
+        collector, track_index + i, *repeated_values,
+        NamedLinesType::kNamedLines, /* is_in_repeat */ true);
+
+    // Subgrids do not support track sizes.
+    if (!is_subgrid) {
+      const GridTrackSize& track_size =
+          track_list.RepeatTrackSize(repeater_index, i);
+      repeated_values->Append(
+          *ComputedStyleUtils::SpecifiedValueForGridTrackSize(track_size,
+                                                              style));
+    }
+  }
+
+  // Standalone grids may have line names after track sizes.
+  if (!is_subgrid) {
+    AddValuesForNamedGridLinesAtIndex(
+        collector, track_index + repeat_size, *repeated_values,
+        NamedLinesType::kNamedLines, /* is_in_repeat */ true);
+  } else if (repeat_size == 0) {
+    // Subgrids allow for empty line definitions.
+    repeated_values->Append(
+        *MakeGarbageCollected<cssvalue::CSSBracketedValueList>());
+  }
+
+  list->Append(*repeated_values);
+
+  return repeat_size * number_of_repetitions;
+}
+
+void PopulateGridTrackListForNonGrid(CSSValueList* list,
+                                     OrderedNamedLinesCollector& collector,
+                                     const blink::NGGridTrackList& track_list,
+                                     const ComputedStyle& style) {
+  const bool is_subgrid = collector.IsSubgriddedAxis();
+  wtf_size_t track_index = 0;
+
+  // Iterate over each repeater. This will cover all tracks because even non
+  // repeats will add repeaters of type `kNoRepeat` to their track list.
+  for (wtf_size_t i = 0; i < track_list.RepeaterCount(); ++i) {
+    switch (track_list.RepeatType(i)) {
+      case NGGridTrackRepeater::RepeatType::kNoRepeat:
+        PopulateNonRepeater(list, collector, track_list, i, track_index, style);
+
+        // Non repeaters always consume one track index.
+        ++track_index;
+        break;
+
+      case NGGridTrackRepeater::RepeatType::kInteger:
+        // Standalone grids can have line names between sizes and repeaters.
+        if (!is_subgrid) {
+          AddValuesForNamedGridLinesAtIndex(collector, track_index, *list,
+                                            NamedLinesType::kNamedLines);
+        }
+        // `PopulateIntegerRepeater` will return the number of tracks populated.
+        // We need to update `track_index` by this value, as the track list
+        // has expanded integer repeaters and interspersed them with
+        // non-repeaters.
+        track_index += PopulateIntegerRepeater(list, collector, track_list, i,
+                                               track_index, style);
+        break;
+
+      case NGGridTrackRepeater::RepeatType::kAutoFill:
+      case NGGridTrackRepeater::RepeatType::kAutoFit:
+        // Standalone grids can have line names between sizes and repeaters.
+        if (!is_subgrid) {
+          AddValuesForNamedGridLinesAtIndex(collector, track_index, *list,
+                                            NamedLinesType::kNamedLines);
+        }
+        PopulateAutoRepeater(list, collector, track_list, i, style);
+
+        // Auto repeaters always consume one track index.
+        ++track_index;
+        break;
+      default:
+        NOTREACHED();
+    }
+  }
+  // Standalone grids can have line names after sizes and repeaters.
+  if (!is_subgrid) {
+    AddValuesForNamedGridLinesAtIndex(collector, track_index, *list,
+                                      NamedLinesType::kNamedLines);
   }
 }
 
@@ -1706,11 +1872,6 @@
   const bool is_for_columns = direction == kForColumns;
   const ComputedGridTrackList& computed_grid_track_list =
       is_for_columns ? style.GridTemplateColumns() : style.GridTemplateRows();
-  const Vector<GridTrackSize, 1>& legacy_track_sizes =
-      computed_grid_track_list.track_sizes.LegacyTrackList();
-  const Vector<GridTrackSize, 1>& auto_repeat_track_sizes =
-      computed_grid_track_list.auto_repeat_track_sizes;
-
   const bool is_layout_grid = layout_object && layout_object->IsLayoutNGGrid();
 
   // Handle the 'none' case.
@@ -1727,14 +1888,22 @@
     is_track_list_empty = positions.size() == 1;
   }
 
-  if (is_track_list_empty) {
+  const bool is_subgrid = computed_grid_track_list.IsSubgriddedAxis();
+
+  // Even if the track list is empty or it's not actually a grid/subgrid in
+  // layout, if the author specified `subgrid`, the computed value should always
+  // begin with `subgrid` and cannot be `none`.
+  CSSValueList* list = CSSValueList::CreateSpaceSeparated();
+  if (is_subgrid) {
+    list->Append(
+        *MakeGarbageCollected<CSSIdentifierValue>(CSSValueID::kSubgrid));
+  } else if (is_track_list_empty) {
     return CSSIdentifierValue::Create(CSSValueID::kNone);
   }
 
-  CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   wtf_size_t auto_repeat_insertion_point =
       computed_grid_track_list.auto_repeat_insertion_point;
-  const bool is_subgrid = computed_grid_track_list.IsSubgriddedAxis();
+  const NGGridTrackList& ng_track_list = computed_grid_track_list.TrackList();
 
   if (is_layout_grid) {
     const auto* grid = ToInterface<LayoutNGGridInterface>(layout_object);
@@ -1753,9 +1922,6 @@
     wtf_size_t start_index = 0;
     wtf_size_t end_index = track_sizes.size();
     if (is_subgrid) {
-      list->Append(
-          *MakeGarbageCollected<CSSIdentifierValue>(CSSValueID::kSubgrid));
-
       // For subgrids, track sizes are not supported. Instead, calculate the end
       // index by subtracting the grid end from its start.
       start_index = grid->ExplicitGridStartForDirection(direction);
@@ -1771,9 +1937,6 @@
         auto_repeat_insertion_point,
         grid->AutoRepeatCountForDirection(direction),
         auto_repeat_track_list_length, is_subgrid);
-    auto GetTrackSize = [&](const LayoutUnit& v) {
-      return ZoomAdjustedPixelValue(v, style);
-    };
     // Named grid line indices are relative to the explicit grid, but we are
     // including all tracks. So we need to subtract the number of leading
     // implicit tracks in order to get the proper line index. This is ignored
@@ -1781,97 +1944,17 @@
     int offset = -base::checked_cast<int>(
         grid->ExplicitGridStartForDirection(direction));
 
-    PopulateGridTrackList(list, collector, track_sizes, GetTrackSize,
-                          start_index, end_index, offset);
+    PopulateGridTrackList(list, collector, track_sizes, style, start_index,
+                          end_index, offset);
     return list;
   }
 
   // Otherwise, the resolved value is the computed value, preserving repeat().
   OrderedNamedLinesCollector collector(
       computed_grid_track_list.ordered_named_grid_lines,
-      computed_grid_track_list.auto_repeat_ordered_named_grid_lines);
-  auto GetTrackSize = [&](const GridTrackSize& v) {
-    return SpecifiedValueForGridTrackSize(v, style);
-  };
-
-  if (auto_repeat_track_sizes.empty()) {
-    // TODO(ansollan): Add support for track lists with auto and integer
-    // repeaters.
-    wtf_size_t track_index = 0;
-    auto AppendValues = [&](CSSValueList* list, const GridTrackSize& track_size,
-                            GridTrackListSerializationType named_line_type) {
-      AddValuesForNamedGridLinesAtIndex(collector, track_index, *list,
-                                        named_line_type);
-      list->Append(*GetTrackSize(track_size));
-      ++track_index;
-    };
-
-    const NGGridTrackList& ng_track_list = computed_grid_track_list.TrackList();
-    for (wtf_size_t i = 0; i < ng_track_list.RepeaterCount(); ++i) {
-      const auto repeat_type = ng_track_list.RepeatType(i);
-
-      // Add the line names and track sizes that aren't part of the repeat.
-      if (repeat_type == NGGridTrackRepeater::RepeatType::kNoRepeat) {
-        AppendValues(list, ng_track_list.RepeatTrackSize(i, 0),
-                     kForNonGridElements);
-        continue;
-      }
-
-      // If a subgridded axis was specified, but the element is not part of a
-      // parent grid, only integer repeats are supported.
-      if (computed_grid_track_list.IsSubgriddedAxis() &&
-          repeat_type != NGGridTrackRepeater::RepeatType::kInteger) {
-        continue;
-      }
-      DCHECK_EQ(repeat_type, NGGridTrackRepeater::RepeatType::kInteger);
-
-      // Add a CSSGridIntegerRepeatValue with the contents of the repeat().
-      const wtf_size_t number_of_repetitions = ng_track_list.RepeatCount(i, 0);
-      const wtf_size_t repeat_size = ng_track_list.RepeatSize(i);
-      CSSValueList* repeated_values =
-          MakeGarbageCollected<cssvalue::CSSGridIntegerRepeatValue>(
-              number_of_repetitions);
-      AddValuesForNamedGridLinesAtIndex(collector, track_index, *list,
-                                        kForNonGridElements);
-      for (wtf_size_t j = 0; j < repeat_size; ++j) {
-        AppendValues(repeated_values, ng_track_list.RepeatTrackSize(i, j),
-                     kForRepeatNonGridElements);
-      }
-      AddValuesForNamedGridLinesAtIndex(
-          collector, track_index, *repeated_values, kForRepeatNonGridElements);
-      list->Append(*repeated_values);
-      // We need to update |track_index| to skip over added grid named lines
-      // that belong to the repeat we just found.
-      track_index += repeat_size * (number_of_repetitions - 1);
-    }
-    AddValuesForNamedGridLinesAtIndex(collector, track_index, *list,
-                                      kForNonGridElements);
-    return list;
-  }
-  // Add the line names and track sizes that precede the auto repeat().
-  PopulateGridTrackList(list, collector, legacy_track_sizes, GetTrackSize,
-                        /* start */ 0,
-                        /* end */ auto_repeat_insertion_point, /* offset */ 0);
-
-  // Add a CSSGridAutoRepeatValue with the contents of the auto repeat().
-  CSSValueList* repeated_values =
-      MakeGarbageCollected<cssvalue::CSSGridAutoRepeatValue>(
-          computed_grid_track_list.auto_repeat_type == AutoRepeatType::kAutoFill
-              ? CSSValueID::kAutoFill
-              : CSSValueID::kAutoFit);
-  OrderedNamedLinesCollectorInsideAutoRepeat repeat_collector(
-      computed_grid_track_list.ordered_named_grid_lines,
-      computed_grid_track_list.auto_repeat_ordered_named_grid_lines);
-  PopulateGridTrackList(repeated_values, repeat_collector,
-                        auto_repeat_track_sizes, GetTrackSize, /* start */ 0,
-                        /* end */ auto_repeat_track_sizes.size(),
-                        /* offset */ 0);
-  list->Append(*repeated_values);
-
-  // Add the line names and track sizes that follow the auto repeat().
-  PopulateGridTrackList(list, collector, legacy_track_sizes, GetTrackSize,
-                        /* start */ auto_repeat_insertion_point,
-                        /* end */ legacy_track_sizes.size(), /* offset */ 1);
+      computed_grid_track_list.auto_repeat_ordered_named_grid_lines, is_subgrid,
+      is_layout_grid);
+  PopulateGridTrackListForNonGrid(list, collector, ng_track_list, style);
   return list;
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
index e6d7268b..f3180e5 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -1286,10 +1286,12 @@
       computed_grid_track_list.auto_repeat_track_sizes;
 
   wtf_size_t current_named_grid_line = 0;
-  auto ConvertLineNameOrTrackSize = [&](const CSSValue& curr_value,
-                                        bool is_in_repeat = false,
-                                        bool is_first_repeat = false) {
+  auto ConvertLineNameOrTrackSize =
+      [&](const CSSValue& curr_value, bool is_in_repeat = false,
+          bool is_first_repeat = false) -> wtf_size_t {
+    wtf_size_t line_name_indices_count = 0;
     if (curr_value.IsGridLineNamesValue()) {
+      ++line_name_indices_count;
       ConvertGridLineNamesList(
           curr_value, current_named_grid_line,
           computed_grid_track_list.named_grid_lines,
@@ -1306,10 +1308,12 @@
       track_sizes.LegacyTrackList().push_back(
           ConvertGridTrackSize(state, curr_value));
     }
+    return line_name_indices_count;
   };
 
   const auto& values = To<CSSValueList>(value);
   auto* curr_value = values.begin();
+  bool is_subgrid = false;
 
   if (RuntimeEnabledFeatures::LayoutNGSubgridEnabled()) {
     auto* identifier_value = DynamicTo<CSSIdentifierValue>(curr_value->Get());
@@ -1317,6 +1321,7 @@
         identifier_value->GetValueID() == CSSValueID::kSubgrid) {
       computed_grid_track_list.axis_type = GridAxisType::kSubgriddedAxis;
       track_list.SetAxisType(GridAxisType::kSubgriddedAxis);
+      is_subgrid = true;
       ++curr_value;
     }
   }
@@ -1326,6 +1331,7 @@
             DynamicTo<cssvalue::CSSGridAutoRepeatValue>(curr_value->Get())) {
       Vector<GridTrackSize, 1> repeated_track_sizes;
       wtf_size_t auto_repeat_index = 0;
+      wtf_size_t line_name_indices_count = 0;
       CSSValueID auto_repeat_id = grid_auto_repeat_value->AutoRepeatID();
       DCHECK(auto_repeat_id == CSSValueID::kAutoFill ||
              auto_repeat_id == CSSValueID::kAutoFit);
@@ -1334,6 +1340,7 @@
                                                     : AutoRepeatType::kAutoFit;
       for (const CSSValue* auto_repeat_value : To<CSSValueList>(**curr_value)) {
         if (auto_repeat_value->IsGridLineNamesValue()) {
+          ++line_name_indices_count;
           ConvertGridLineNamesList(
               *auto_repeat_value, auto_repeat_index,
               computed_grid_track_list.auto_repeat_named_grid_lines,
@@ -1351,7 +1358,8 @@
                              static_cast<NGGridTrackRepeater::RepeatType>(
                                  computed_grid_track_list.auto_repeat_type),
                              /* repeat_count */ 1,
-                             /* repeat_number_of_lines */ auto_repeat_index);
+                             /* repeat_number_of_lines */ auto_repeat_index,
+                             line_name_indices_count);
       DCHECK(auto_repeat_track_sizes.empty());
       auto_repeat_track_sizes = std::move(repeated_track_sizes);
       computed_grid_track_list.auto_repeat_insertion_point =
@@ -1362,33 +1370,49 @@
     if (auto* grid_integer_repeat_value =
             DynamicTo<cssvalue::CSSGridIntegerRepeatValue>(curr_value->Get())) {
       const wtf_size_t repetitions = grid_integer_repeat_value->Repetitions();
+      wtf_size_t line_name_indices_count = 0;
 
       for (wtf_size_t i = 0; i < repetitions; ++i) {
+        const bool is_first_repeat = i == 0;
         for (auto integer_repeat_value : *grid_integer_repeat_value) {
-          ConvertLineNameOrTrackSize(*integer_repeat_value,
-                                     /* is_inside_repeat */ true,
-                                     /* is_first_repeat */ i == 0);
+          wtf_size_t current_line_name_indices_count =
+              ConvertLineNameOrTrackSize(*integer_repeat_value,
+                                         /* is_inside_repeat */ true,
+                                         is_first_repeat);
+          // Only add to `line_name_indices_count` on the first iteration so it
+          // doesn't need to be divided by `repetitions`.
+          if (is_first_repeat) {
+            line_name_indices_count += current_line_name_indices_count;
+          }
         }
       }
 
+      Vector<GridTrackSize, 1> repeater_track_sizes;
       if (computed_grid_track_list.axis_type == GridAxisType::kStandaloneAxis) {
-        Vector<GridTrackSize, 1> repeater_track_sizes;
         for (auto integer_repeat_value : *grid_integer_repeat_value) {
           if (!integer_repeat_value->IsGridLineNamesValue()) {
             repeater_track_sizes.push_back(
                 ConvertGridTrackSize(state, *integer_repeat_value));
           }
         }
-        track_list.AddRepeater(repeater_track_sizes,
-                               NGGridTrackRepeater::RepeatType::kInteger,
-                               repetitions);
       }
+      track_list.AddRepeater(repeater_track_sizes,
+                             NGGridTrackRepeater::RepeatType::kInteger,
+                             repetitions, /* repeat_number_of_lines */ 1u,
+                             line_name_indices_count);
       continue;
     }
 
-    ConvertLineNameOrTrackSize(**curr_value);
+    wtf_size_t line_name_indices_count =
+        ConvertLineNameOrTrackSize(**curr_value);
     if (!curr_value->Get()->IsGridLineNamesValue()) {
       track_list.AddRepeater({ConvertGridTrackSize(state, **curr_value)});
+    } else if (is_subgrid) {
+      track_list.AddRepeater(/* repeater_track_sizes */ {},
+                             NGGridTrackRepeater::RepeatType::kNoRepeat,
+                             /* repeat_count */ 1,
+                             /* repeat_number_of_lines */ 1u,
+                             line_name_indices_count);
     }
   }
 
@@ -2569,7 +2593,7 @@
     bool is_animation_tainted) {
   // TODO(andruud): Produce tokens directly from CSSValue.
   return CSSVariableData::Create(value.CssText(), is_animation_tainted,
-                                 /*needs_variable_resolution=*/false);
+                                 /* needs_variable_resolution */ false);
 }
 
 namespace {
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
index 4c0a2a6..02000cc8f0 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
@@ -45,7 +45,6 @@
 #include "third_party/blink/renderer/core/style/basic_shapes.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/style/grid_area.h"
-#include "third_party/blink/renderer/core/style/grid_positions_resolver.h"
 #include "third_party/blink/renderer/core/style/named_grid_lines_map.h"
 #include "third_party/blink/renderer/core/style/ordered_named_grid_lines.h"
 #include "third_party/blink/renderer/core/style/shadow_list.h"
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc
index 2e8ec69..bbb0f734 100644
--- a/third_party/blink/renderer/core/css/selector_checker.cc
+++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -1691,6 +1691,9 @@
       break;
     }
     case CSSSelector::kPseudoOpen:
+      if (auto* selectmenu = DynamicTo<HTMLSelectMenuElement>(element)) {
+        return selectmenu->open();
+      }
       if (auto* html_element = DynamicTo<HTMLElement>(element);
           html_element && html_element->HasPopoverAttribute()) {
         return html_element->popoverOpen();
@@ -1704,6 +1707,9 @@
         // important to *match* when the feature is *disabled*.
         return true;
       }
+      if (auto* selectmenu = DynamicTo<HTMLSelectMenuElement>(element)) {
+        return !selectmenu->open();
+      }
       if (auto* html_element = DynamicTo<HTMLElement>(element);
           html_element && html_element->HasPopoverAttribute()) {
         return html_element->GetPopoverData()->visibilityState() ==
diff --git a/third_party/blink/renderer/core/exported/build.gni b/third_party/blink/renderer/core/exported/build.gni
index b152ebe8..b3653a9 100644
--- a/third_party/blink/renderer/core/exported/build.gni
+++ b/third_party/blink/renderer/core/exported/build.gni
@@ -79,6 +79,7 @@
   "web_document_test.cc",
   "web_drag_data_test.cc",
   "web_element_test.cc",
+  "web_form_control_element_test.cc",
   "web_frame_serializer_sanitization_test.cc",
   "web_frame_serializer_test.cc",
   "web_frame_serializer_test_helper.cc",
diff --git a/third_party/blink/renderer/core/exported/web_form_control_element.cc b/third_party/blink/renderer/core/exported/web_form_control_element.cc
index fcfad52..b256f0b2 100644
--- a/third_party/blink/renderer/core/exported/web_form_control_element.cc
+++ b/third_party/blink/renderer/core/exported/web_form_control_element.cc
@@ -30,9 +30,13 @@
 
 #include "third_party/blink/public/web/web_form_control_element.h"
 
+#include "base/time/time.h"
+#include "third_party/blink/public/common/input/web_input_event.h"
+#include "third_party/blink/public/common/input/web_keyboard_event.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
+#include "third_party/blink/renderer/core/events/keyboard_event.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
@@ -40,6 +44,8 @@
 #include "third_party/blink/renderer/core/html/forms/html_select_menu_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
+#include "third_party/blink/renderer/platform/keyboard_codes.h"
+#include "ui/events/keycodes/dom/dom_key.h"
 
 #include "base/memory/scoped_refptr.h"
 
@@ -184,12 +190,33 @@
   if (IsA<HTMLInputElement>(*private_) || IsA<HTMLTextAreaElement>(*private_)) {
     if (!Focused())
       DispatchFocusEvent();
-    Unwrap<Element>()->DispatchScopedEvent(
-        *Event::CreateBubble(event_type_names::kKeydown));
+
+    auto send_event = [local_dom_window =
+                           Unwrap<Element>()->GetDocument().domWindow(),
+                       this](WebInputEvent::Type event_type) {
+      WebKeyboardEvent web_event{event_type, WebInputEvent::kNoModifiers,
+                                 base::TimeTicks::Now()};
+      web_event.dom_key = ui::DomKey::UNIDENTIFIED;
+      web_event.dom_code = static_cast<int>(ui::DomKey::UNIDENTIFIED);
+      web_event.native_key_code = blink::VKEY_UNKNOWN;
+      web_event.windows_key_code = blink::VKEY_UNKNOWN;
+      web_event.text[0] = blink::VKEY_UNKNOWN;
+      web_event.unmodified_text[0] = blink::VKEY_UNKNOWN;
+
+      KeyboardEvent* event = KeyboardEvent::Create(web_event, local_dom_window);
+      Unwrap<Element>()->DispatchScopedEvent(*event);
+    };
+
+    // Simulate key events in case the website checks via JS that a keyboard
+    // interaction took place.
+    send_event(WebInputEvent::Type::kRawKeyDown);
+
     Unwrap<TextControlElement>()->SetAutofillValue(
         value, value.IsEmpty() ? WebAutofillState::kNotFilled : autofill_state);
-    Unwrap<Element>()->DispatchScopedEvent(
-        *Event::CreateBubble(event_type_names::kKeyup));
+
+    send_event(WebInputEvent::Type::kChar);
+    send_event(WebInputEvent::Type::kKeyUp);
+
     if (!Focused())
       DispatchBlurEvent();
   } else if (auto* select = ::blink::DynamicTo<HTMLSelectElement>(*private_)) {
diff --git a/third_party/blink/renderer/core/exported/web_form_control_element_test.cc b/third_party/blink/renderer/core/exported/web_form_control_element_test.cc
new file mode 100644
index 0000000..1b849be3
--- /dev/null
+++ b/third_party/blink/renderer/core/exported/web_form_control_element_test.cc
@@ -0,0 +1,91 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/web/web_form_control_element.h"
+
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/web/web_autofill_state.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
+#include "third_party/blink/renderer/core/event_type_names.h"
+#include "third_party/blink/renderer/core/events/keyboard_event.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+
+namespace blink {
+
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Values;
+
+// A fake event listener that logs keys and codes of observed keyboard events.
+class FakeEventListener final : public NativeEventListener {
+ public:
+  void Invoke(ExecutionContext*, Event* event) override {
+    KeyboardEvent* keyboard_event = DynamicTo<KeyboardEvent>(event);
+    if (!event) {
+      return;
+    }
+    codes_.push_back(keyboard_event->code());
+    keys_.push_back(keyboard_event->key());
+  }
+
+  const std::vector<String>& codes() const { return codes_; }
+  const std::vector<String>& keys() const { return keys_; }
+
+ private:
+  std::vector<String> codes_;
+  std::vector<String> keys_;
+};
+
+}  // namespace
+
+class WebFormControlElementTest
+    : public PageTestBase,
+      public testing::WithParamInterface<const char*> {
+ protected:
+  void InsertHTML() {
+    GetDocument().documentElement()->setInnerHTML(GetParam());
+  }
+
+  WebFormControlElement TestElement() {
+    HTMLFormControlElement* control_element = DynamicTo<HTMLFormControlElement>(
+        GetDocument().getElementById("testElement"));
+    DCHECK(control_element);
+    return WebFormControlElement(control_element);
+  }
+};
+
+TEST_P(WebFormControlElementTest, SetAutofillValue) {
+  InsertHTML();
+  WebFormControlElement element = TestElement();
+  auto* keypress_handler = MakeGarbageCollected<FakeEventListener>();
+  element.Unwrap<HTMLFormControlElement>()->addEventListener(
+      event_type_names::kKeydown, keypress_handler);
+
+  EXPECT_EQ(TestElement().Value(), "test value");
+  EXPECT_EQ(element.GetAutofillState(), WebAutofillState::kNotFilled);
+
+  // We expect to see one "fake" key press event with an unidentified key.
+  element.SetAutofillValue("new value", WebAutofillState::kAutofilled);
+  EXPECT_EQ(element.Value(), "new value");
+  EXPECT_EQ(element.GetAutofillState(), WebAutofillState::kAutofilled);
+  EXPECT_THAT(keypress_handler->codes(), ElementsAre(""));
+  EXPECT_THAT(keypress_handler->keys(), ElementsAre("Unidentified"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    WebFormControlElementTest,
+    Values("<input type='text' id=testElement value='test value'>",
+           "<textarea id=testElement>test value</textarea>"));
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index bb5b2ec..4f99cf1 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -3835,7 +3835,6 @@
 
   if (args.page_scale_delta != 1) {
     double_tap_zoom_pending_ = false;
-    visual_viewport.UserDidChangeScale();
   }
 
   elastic_overscroll_ += args.elastic_overscroll_delta;
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.cc b/third_party/blink/renderer/core/frame/visual_viewport.cc
index 0ffd6827..f2199e8 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport.cc
@@ -95,8 +95,6 @@
       scale_(1),
       is_pinch_gesture_active_(false),
       browser_controls_adjustment_(0),
-      max_page_scale_(-1),
-      track_pinch_zoom_stats_for_page_(false),
       needs_paint_property_update_(true),
       overscroll_type_(ComputeOverscrollType()) {
   UniqueObjectId unique_id = NewUniqueObjectId();
@@ -370,9 +368,7 @@
   return change;
 }
 
-VisualViewport::~VisualViewport() {
-  SendUMAMetrics();
-}
+VisualViewport::~VisualViewport() = default;
 
 void VisualViewport::Trace(Visitor* visitor) const {
   visitor->Trace(page_);
@@ -1074,48 +1070,6 @@
       RootFrameToViewport(gfx::PointF(point_in_root_frame)));
 }
 
-void VisualViewport::StartTrackingPinchStats() {
-  DCHECK(IsActiveViewport());
-
-  Document* document = LocalMainFrame().GetDocument();
-  if (!document)
-    return;
-
-  if (!document->Url().ProtocolIsInHTTPFamily())
-    return;
-
-  track_pinch_zoom_stats_for_page_ = !ShouldDisableDesktopWorkarounds();
-}
-
-void VisualViewport::UserDidChangeScale() {
-  DCHECK(IsActiveViewport());
-  if (!track_pinch_zoom_stats_for_page_)
-    return;
-
-  max_page_scale_ = std::max(max_page_scale_, scale_);
-}
-
-void VisualViewport::SendUMAMetrics() {
-  if (track_pinch_zoom_stats_for_page_) {
-    bool did_scale = max_page_scale_ > 0;
-
-    base::UmaHistogramBoolean("Viewport.DidScalePage", did_scale);
-
-    if (did_scale) {
-      int zoom_percentage = floor(max_page_scale_ * 100);
-
-      // Note: while defined as an exact linear histogram with 21 buckets here,
-      // the UMA itself is tagged as an enumeration (PageScaleFactor) in
-      // histograms.xml to make it easy to identify the buckets...
-      int bucket = floor(zoom_percentage / 25.f);
-      base::UmaHistogramExactLinear("Viewport.MaxPageScale", bucket, 21);
-    }
-  }
-
-  max_page_scale_ = -1;
-  track_pinch_zoom_stats_for_page_ = false;
-}
-
 bool VisualViewport::ShouldDisableDesktopWorkarounds() const {
   DCHECK(IsActiveViewport());
 
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.h b/third_party/blink/renderer/core/frame/visual_viewport.h
index 68d1f97..594ac88 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.h
+++ b/third_party/blink/renderer/core/frame/visual_viewport.h
@@ -260,11 +260,6 @@
   double VisibleWidthCSSPx() const;
   double VisibleHeightCSSPx() const;
 
-  // Used for gathering data on user pinch-zoom statistics.
-  void UserDidChangeScale();
-  void SendUMAMetrics();
-  void StartTrackingPinchStats();
-
   // Heuristic-based function for determining if we should disable workarounds
   // for viewing websites that are not optimized for mobile devices.
   bool ShouldDisableDesktopWorkarounds() const;
@@ -388,11 +383,6 @@
   // they expand or shrink the visible content height.
   float browser_controls_adjustment_;
 
-  // The maximum page scale the user has zoomed to on the current page. Used
-  // only to report statistics about pinch-zoom usage.
-  float max_page_scale_;
-  bool track_pinch_zoom_stats_for_page_;
-
   // For page scale animation on page_scale_node_.
   CompositorElementId page_scale_element_id_;
   // For scrolling, on scroll_layer_, scroll_node_, and scroll element ids of
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index d0fa44b..ae1e869 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -2680,10 +2680,6 @@
   if (IsProvisional())
     return WebInputEventResult::kHandledSuppressed;
 
-  // Only record metrics for the root frame.
-  if (ForTopMostMainFrame())
-    GetPage()->GetVisualViewport().StartTrackingPinchStats();
-
   // If a drag-and-drop operation is in progress, ignore input events except
   // PointerCancel and GestureLongPress.
   if (doing_drag_and_drop_ &&
diff --git a/third_party/blink/renderer/core/html/forms/html_field_set_element.cc b/third_party/blink/renderer/core/html/forms/html_field_set_element.cc
index df3d428..0805152 100644
--- a/third_party/blink/renderer/core/html/forms/html_field_set_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_field_set_element.cc
@@ -168,4 +168,14 @@
   return EnsureCachedCollection<HTMLCollection>(kFormControls);
 }
 
+bool HTMLFieldSetElement::IsDisabledFormControl() const {
+  if (RuntimeEnabledFeatures::SendMouseEventsDisabledFormControlsEnabled()) {
+    // The fieldset element itself should never be considered disabled, it is
+    // only supposed to affect its descendants:
+    // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-disabled
+    return false;
+  }
+  return HTMLFormControlElement::IsDisabledFormControl();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/html_field_set_element.h b/third_party/blink/renderer/core/html/forms/html_field_set_element.h
index f812ff38..b90df5b 100644
--- a/third_party/blink/renderer/core/html/forms/html_field_set_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_field_set_element.h
@@ -40,6 +40,8 @@
   HTMLLegendElement* Legend() const;
   HTMLCollection* elements();
 
+  bool IsDisabledFormControl() const override;
+
  protected:
   void DisabledAttributeChanged() override;
 
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index 3bfef2a..2693c43 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -63,6 +63,7 @@
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
 #include "third_party/blink/renderer/core/layout/layout_object_factory.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/spatial_navigation.h"
@@ -398,8 +399,9 @@
 LayoutObject* HTMLSelectElement::CreateLayoutObject(
     const ComputedStyle& style,
     LegacyLayout legacy_layout) {
-  if (UsesMenuList())
-    return LayoutObjectFactory::CreateFlexibleBox(*this, style, legacy_layout);
+  if (UsesMenuList()) {
+    return MakeGarbageCollected<LayoutNGFlexibleBox>(this);
+  }
   return LayoutObjectFactory::CreateBlockFlow(*this, style, legacy_layout);
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
index 2bd4dba..141729f5 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
@@ -368,6 +368,8 @@
 void HTMLSelectMenuElement::OpenListbox() {
   if (listbox_part_ && !open()) {
     listbox_part_->showPopover(ASSERT_NO_EXCEPTION);
+    PseudoStateChanged(CSSSelector::kPseudoClosed);
+    PseudoStateChanged(CSSSelector::kPseudoOpen);
     if (selectedOption()) {
       selectedOption()->Focus();
     }
@@ -383,6 +385,8 @@
           HidePopoverFocusBehavior::kNone,
           HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
           /*exception_state=*/nullptr);
+      PseudoStateChanged(CSSSelector::kPseudoClosed);
+      PseudoStateChanged(CSSSelector::kPseudoOpen);
     }
     if (button_part_) {
       button_part_->Focus();
diff --git a/third_party/blink/renderer/core/html/forms/listed_element.cc b/third_party/blink/renderer/core/html/forms/listed_element.cc
index 8bd6ae7..6a572898 100644
--- a/third_party/blink/renderer/core/html/forms/listed_element.cc
+++ b/third_party/blink/renderer/core/html/forms/listed_element.cc
@@ -119,7 +119,7 @@
 void ListedElement::InsertedInto(ContainerNode& insertion_point) {
   ancestor_disabled_state_ = AncestorDisabledState::kUnknown;
   // Force traversal to find ancestor
-  may_have_field_set_ancestor_ = true;
+  may_have_fieldset_ancestor_ = true;
   data_list_ancestor_state_ = DataListAncestorState::kUnknown;
   UpdateWillValidateCache();
 
@@ -275,7 +275,7 @@
 void ListedElement::FieldSetAncestorsSetNeedsValidityCheck(Node* node) {
   if (!node)
     return;
-  if (!may_have_field_set_ancestor_)
+  if (!may_have_fieldset_ancestor_)
     return;
   for (auto* field_set =
            Traversal<HTMLFieldSetElement>::FirstAncestorOrSelf(*node);
@@ -593,11 +593,11 @@
 }
 
 void ListedElement::UpdateAncestorDisabledState() const {
-  if (!may_have_field_set_ancestor_) {
+  if (!may_have_fieldset_ancestor_) {
     ancestor_disabled_state_ = AncestorDisabledState::kEnabled;
     return;
   }
-  may_have_field_set_ancestor_ = false;
+  may_have_fieldset_ancestor_ = false;
   // <fieldset> element of which |disabled| attribute affects the
   // target element.
   HTMLFieldSetElement* disabled_fieldset_ancestor = nullptr;
@@ -608,15 +608,17 @@
       last_legend_ancestor = ancestor;
       continue;
     }
-    if (!IsA<HTMLFieldSetElement>(*ancestor))
-      continue;
-    may_have_field_set_ancestor_ = true;
-    if (ancestor->IsDisabledFormControl()) {
-      auto* fieldset = To<HTMLFieldSetElement>(ancestor);
-      if (last_legend_ancestor && last_legend_ancestor == fieldset->Legend())
-        continue;
-      disabled_fieldset_ancestor = fieldset;
-      break;
+    if (HTMLFieldSetElement* fieldset_ancestor =
+            DynamicTo<HTMLFieldSetElement>(ancestor)) {
+      may_have_fieldset_ancestor_ = true;
+      if (fieldset_ancestor->is_element_disabled_) {
+        if (last_legend_ancestor &&
+            last_legend_ancestor == fieldset_ancestor->Legend()) {
+          continue;
+        }
+        disabled_fieldset_ancestor = fieldset_ancestor;
+        break;
+      }
     }
   }
   ancestor_disabled_state_ = disabled_fieldset_ancestor
diff --git a/third_party/blink/renderer/core/html/forms/listed_element.h b/third_party/blink/renderer/core/html/forms/listed_element.h
index ca6236d..98e9bba 100644
--- a/third_party/blink/renderer/core/html/forms/listed_element.h
+++ b/third_party/blink/renderer/core/html/forms/listed_element.h
@@ -198,9 +198,9 @@
   void SetCustomValidationMessage(const String& message);
 
   // False; There are no FIELDSET ancestors.
-  // True; There might be a FIELDSET ancestor, and thre might be no
+  // True; There might be a FIELDSET ancestor, and there might be no
   //       FIELDSET ancestors.
-  mutable bool may_have_field_set_ancestor_ = true;
+  mutable bool may_have_fieldset_ancestor_ = true;
 
  private:
   void UpdateAncestorDisabledState() const;
diff --git a/third_party/blink/renderer/core/html/forms/range_input_type.cc b/third_party/blink/renderer/core/html/forms/range_input_type.cc
index 8527ad3..6e31fa29b 100644
--- a/third_party/blink/renderer/core/html/forms/range_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/range_input_type.cc
@@ -56,7 +56,7 @@
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/core/layout/layout_block.h"
-#include "third_party/blink/renderer/core/layout/layout_object_factory.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
@@ -255,9 +255,8 @@
 
 LayoutObject* RangeInputType::CreateLayoutObject(const ComputedStyle& style,
                                                  LegacyLayout legacy) const {
-  // TODO(crbug.com/1131352): input[type=range] should not use
-  // LayoutFlexibleBox.
-  return LayoutObjectFactory::CreateFlexibleBox(GetElement(), style, legacy);
+  // TODO(crbug.com/1131352): input[type=range] should not use flexbox.
+  return MakeGarbageCollected<LayoutNGFlexibleBox>(&GetElement());
 }
 
 Decimal RangeInputType::ParseToNumber(const String& src,
diff --git a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
index 265ed91..0f6eb4b3 100644
--- a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
+++ b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
@@ -46,6 +46,7 @@
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
 #include "third_party/blink/renderer/core/layout/layout_object_factory.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h"
 #include "ui/base/ui_base_features.h"
 
 namespace blink {
@@ -317,7 +318,7 @@
 LayoutObject* SliderContainerElement::CreateLayoutObject(
     const ComputedStyle& style,
     LegacyLayout legacy) {
-  return LayoutObjectFactory::CreateFlexibleBox(*this, style, legacy);
+  return MakeGarbageCollected<LayoutNGFlexibleBox>(this);
 }
 
 void SliderContainerElement::DefaultEventHandler(Event& event) {
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc
index d04cf7f..320ebd8d 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -285,7 +285,8 @@
       UseCounter::Count(GetDocument(), WebFeature::kIFrameCSPAttribute);
     }
   } else if (name == html_names::kBrowsingtopicsAttr) {
-    if (RuntimeEnabledFeatures::TopicsAPIEnabled(GetExecutionContext())) {
+    if (RuntimeEnabledFeatures::TopicsAPIEnabled(GetExecutionContext()) &&
+        GetExecutionContext()->IsSecureContext()) {
       bool old_browsing_topics = !params.old_value.IsNull();
       bool new_browsing_topics = !params.new_value.IsNull();
 
@@ -585,8 +586,12 @@
   auto attributes = mojom::blink::IframeAttributes::New();
   attributes->parsed_csp_attribute = csp.empty() ? nullptr : std::move(csp[0]);
   attributes->credentialless = credentialless_;
-  attributes->browsing_topics =
-      !FastGetAttribute(html_names::kBrowsingtopicsAttr).IsNull();
+
+  if (RuntimeEnabledFeatures::TopicsAPIEnabled(GetExecutionContext()) &&
+      GetExecutionContext()->IsSecureContext()) {
+    attributes->browsing_topics =
+        !FastGetAttribute(html_names::kBrowsingtopicsAttr).IsNull();
+  }
 
   attributes->id = ConvertToReportValue(id_);
   attributes->name = ConvertToReportValue(name_);
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index 1a69edfb..a8e50a6 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -28,7 +28,6 @@
 #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
 #include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -39,7 +38,6 @@
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
-#include "third_party/blink/renderer/core/style/grid_positions_resolver.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
 #include "third_party/blink/renderer/platform/graphics/path.h"
 #include "third_party/blink/renderer/platform/text/writing_mode.h"
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni
index 0d82c1bd..127763c 100644
--- a/third_party/blink/renderer/core/layout/build.gni
+++ b/third_party/blink/renderer/core/layout/build.gni
@@ -128,8 +128,6 @@
   "layout_embedded_object.h",
   "layout_fieldset.cc",
   "layout_fieldset.h",
-  "layout_flexible_box.cc",
-  "layout_flexible_box.h",
   "layout_flow_thread.cc",
   "layout_flow_thread.h",
   "layout_frame.cc",
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
index 94dabe8..07972fa 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
@@ -32,7 +32,6 @@
 
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/min_max_sizes.h"
 #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
@@ -77,7 +76,6 @@
 }  // namespace
 
 FlexItem::FlexItem(const FlexLayoutAlgorithm* algorithm,
-                   LayoutBox* box,
                    const ComputedStyle& style,
                    LayoutUnit flex_base_content_size,
                    MinMaxSizes min_max_main_sizes,
@@ -91,7 +89,6 @@
                    bool depends_on_min_max_sizes)
     : algorithm_(algorithm),
       line_number_(0),
-      box_(box),
       style_(style),
       flex_base_content_size_(flex_base_content_size),
       min_max_main_sizes_(min_max_main_sizes),
@@ -182,13 +179,6 @@
 
 LayoutUnit FlexItem::MarginBoxAscent(bool is_last_baseline,
                                      bool is_wrap_reverse) const {
-  if (box_) {
-    LayoutUnit ascent(box_->FirstLineBoxBaseline());
-    if (ascent == -1)
-      ascent = cross_axis_size_;
-    return ascent + FlowAwareMarginBefore();
-  }
-
   DCHECK(layout_result_);
   NGBoxFragment baseline_fragment(
       baseline_writing_direction_,
@@ -294,17 +284,6 @@
   LayoutUnit stretched_size =
       std::max(cross_axis_border_padding_,
                Line()->cross_axis_extent_ - CrossAxisMarginExtent());
-  if (box_) {
-    if (MainAxisIsInlineAxis() && style_.LogicalHeight().IsAuto()) {
-      cross_axis_size_ = box_->ConstrainLogicalHeightByMinMax(
-          stretched_size, box_->IntrinsicContentLogicalHeight());
-    } else if (!MainAxisIsInlineAxis() && style_.LogicalWidth().IsAuto()) {
-      const auto* flexbox = To<LayoutFlexibleBox>(box_->Parent());
-      cross_axis_size_ = box_->ConstrainLogicalWidthByMinMax(
-          stretched_size, flexbox->CrossAxisContentExtent(), flexbox);
-    }
-    return;
-  }
 
   if ((MainAxisIsInlineAxis() && style_.LogicalHeight().IsAuto()) ||
       (!MainAxisIsInlineAxis() && style_.LogicalWidth().IsAuto())) {
@@ -314,7 +293,6 @@
 }
 
 void FlexItem::Trace(Visitor* visitor) const {
-  visitor->Trace(box_);
   visitor->Trace(ng_input_node_);
   visitor->Trace(layout_result_);
 }
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
index 3469508..50434562 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
@@ -50,7 +50,6 @@
 class FlexLine;
 class FlexLayoutAlgorithm;
 class NGFlexLayoutAlgorithm;
-class LayoutBox;
 struct MinMaxSizes;
 struct NGFlexLine;
 
@@ -68,52 +67,6 @@
 
 typedef HeapVector<FlexItem, 8> FlexItemVector;
 
-class AutoClearOverrideLogicalHeight {
-  STACK_ALLOCATED();
-
- public:
-  explicit AutoClearOverrideLogicalHeight(LayoutBox* box)
-      : box_(box), old_override_height_(-1) {
-    if (box_ && box_->HasOverrideLogicalHeight()) {
-      old_override_height_ = box_->OverrideLogicalHeight();
-      box_->ClearOverrideLogicalHeight();
-    }
-  }
-  ~AutoClearOverrideLogicalHeight() {
-    if (old_override_height_ != LayoutUnit(-1)) {
-      DCHECK(box_);
-      box_->SetOverrideLogicalHeight(old_override_height_);
-    }
-  }
-
- private:
-  LayoutBox* box_;
-  LayoutUnit old_override_height_;
-};
-
-class AutoClearOverrideLogicalWidth {
-  STACK_ALLOCATED();
-
- public:
-  explicit AutoClearOverrideLogicalWidth(LayoutBox* box)
-      : box_(box), old_override_width_(-1) {
-    if (box_ && box_->HasOverrideLogicalWidth()) {
-      old_override_width_ = box_->OverrideLogicalWidth();
-      box_->ClearOverrideLogicalWidth();
-    }
-  }
-  ~AutoClearOverrideLogicalWidth() {
-    if (old_override_width_ != LayoutUnit(-1)) {
-      DCHECK(box_);
-      box_->SetOverrideLogicalWidth(old_override_width_);
-    }
-  }
-
- private:
-  LayoutBox* box_;
-  LayoutUnit old_override_width_;
-};
-
 class FlexItem {
   DISALLOW_NEW();
 
@@ -125,7 +78,6 @@
   //   border/padding.
   //   |min_max_cross_sizes| does include cross_axis_border_padding.
   FlexItem(const FlexLayoutAlgorithm*,
-           LayoutBox*,
            const ComputedStyle& style,
            LayoutUnit flex_base_content_size,
            MinMaxSizes min_max_main_sizes,
@@ -200,7 +152,6 @@
 
   const FlexLayoutAlgorithm* algorithm_;
   wtf_size_t line_number_;
-  Member<LayoutBox> box_;
   const ComputedStyle& style_;
   const LayoutUnit flex_base_content_size_;
   const MinMaxSizes min_max_main_sizes_;
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index bcf583d..70c2d01 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -49,7 +49,6 @@
 #include "third_party/blink/renderer/core/layout/box_layout_extra_input.h"
 #include "third_party/blink/renderer/core/layout/hit_test_location.h"
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/layout_flow_thread.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h"
@@ -60,6 +59,7 @@
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
@@ -962,30 +962,6 @@
   if (positioned_object->NeedsLayout())
     positioned_object->UpdateLayout();
 
-  LayoutObject* parent = positioned_object->Parent();
-  bool layout_changed = false;
-  if ((parent->IsLayoutNGFlexibleBox() &&
-       !positioned_object->IsLayoutNGObject() &&
-       LayoutFlexibleBox::SetStaticPositionForChildInFlexNGContainer(
-           *positioned_object, To<LayoutBlock>(parent))) ||
-      (parent->IsFlexibleBox() &&
-       To<LayoutFlexibleBox>(parent)->SetStaticPositionForPositionedLayout(
-           *positioned_object))) {
-    // The static position of an abspos child of a flexbox depends on its size
-    // (for example, they can be centered). So we may have to reposition the
-    // item after layout.
-    // TODO(cbiesinger): We could probably avoid a layout here and just
-    // reposition?
-    positioned_object->ForceLayout();
-    layout_changed = true;
-  }
-
-  // Lay out again if our estimate was wrong.
-  if (!layout_changed && needs_block_direction_location_set_before_layout &&
-      logical_top_estimate != LogicalTopForChild(*positioned_object)) {
-    positioned_object->ForceLayout();
-  }
-
   if (is_paginated)
     UpdateFragmentationInfoForChild(*positioned_object);
 }
@@ -1600,9 +1576,7 @@
   // values for width.
   const ComputedStyle& style_to_use = StyleRef();
   if (!IsTableCell() && style_to_use.LogicalWidth().IsFixed() &&
-      style_to_use.LogicalWidth().Value() >= 0 &&
-      !(IsFlexItemCommon() && Parent()->StyleRef().IsDeprecatedWebkitBox() &&
-        !style_to_use.LogicalWidth().IntValue())) {
+      style_to_use.LogicalWidth().Value() >= 0) {
     sizes = AdjustBorderBoxLogicalWidthForBoxSizing(
         LayoutUnit(style_to_use.LogicalWidth().Value()));
   } else {
@@ -2268,8 +2242,8 @@
 
   LayoutBlock* layout_block;
   if (new_display == EDisplay::kFlex) {
-    layout_block = LayoutObjectFactory::CreateFlexibleBox(parent->GetDocument(),
-                                                          *new_style, legacy);
+    layout_block =
+        MakeGarbageCollected<LayoutNGFlexibleBox>(/* element */ nullptr);
   } else if (new_display == EDisplay::kGrid) {
     layout_block = MakeGarbageCollected<LayoutNGGrid>(/* element */ nullptr);
   } else if (new_display == EDisplay::kBlockMath) {
@@ -2475,11 +2449,7 @@
        (!style.LogicalTop().IsAuto() && !style.LogicalBottom().IsAuto()));
 
   LayoutUnit stretched_flex_height(-1);
-  if (IsFlexItem()) {
-    const auto* flex_box = To<LayoutFlexibleBox>(Parent());
-    if (flex_box->UseOverrideLogicalHeightForPerentageResolution(*this))
-      stretched_flex_height = OverrideContentLogicalHeight();
-  } else if (HasOverrideLogicalHeight() && IsOverrideLogicalHeightDefinite()) {
+  if (HasOverrideLogicalHeight() && IsOverrideLogicalHeightDefinite()) {
     stretched_flex_height = OverrideContentLogicalHeight();
   }
   if (stretched_flex_height != LayoutUnit(-1)) {
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index bf20e95..bc3776d 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -66,7 +66,6 @@
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_fieldset.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_list_marker.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h"
@@ -2639,6 +2638,7 @@
 // rules if they can improve LCD text.
 bool LayoutBox::TextIsKnownToBeOnOpaqueBackground() const {
   NOT_DESTROYED();
+  DCHECK(!RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled());
   // Text may overflow the background area.
   if (!ShouldClipOverflowAlongEitherAxis())
     return false;
@@ -4407,18 +4407,6 @@
       return true;
   }
 
-  // Flexible horizontal boxes lay out children at their intrinsic widths. Also
-  // vertical boxes that don't stretch their kids lay out their children at
-  // their intrinsic widths.
-  // FIXME: Think about writing-mode here.
-  // https://bugs.webkit.org/show_bug.cgi?id=46473
-  if ((Parent()->StyleRef().IsDeprecatedWebkitBox() &&
-       Parent()->IsFlexibleBox()) &&
-      (Parent()->StyleRef().BoxOrient() == EBoxOrient::kHorizontal ||
-       Parent()->StyleRef().BoxAlign() != EBoxAlignment::kStretch)) {
-    return true;
-  }
-
   // Button, input, select, textarea, and legend treat width value of 'auto' as
   // 'intrinsic' unless it's in a stretching column flexbox.
   // FIXME: Think about writing-mode here.
@@ -5229,11 +5217,6 @@
       auto* block = DynamicTo<LayoutBlock>(cb);
       if (block) {
         block->AddPercentHeightDescendant(const_cast<LayoutBox*>(this));
-        if (block->IsFlexItem()) {
-          const auto* flex_box = To<LayoutFlexibleBox>(block->Parent());
-          if (flex_box->UseOverrideLogicalHeightForPerentageResolution(*block))
-            stretched_height = block->OverrideContentLogicalHeight();
-        }
       }
 
       LayoutUnit available_height;
@@ -5318,12 +5301,8 @@
   }
 
   if (IsFlexItemIncludingNG()) {
-    if (IsFlexItem()) {
-      const auto& flex_box = To<LayoutFlexibleBox>(*Parent());
-      if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this))
-        return OverrideContentLogicalHeight();
-    } else if (HasOverrideContainingBlockContentLogicalWidth() &&
-               IsOrthogonalWritingModeRoot()) {
+    if (HasOverrideContainingBlockContentLogicalWidth() &&
+        IsOrthogonalWritingModeRoot()) {
       return OverrideContainingBlockContentLogicalWidth();
     } else if (HasOverrideLogicalHeight() &&
                IsOverrideLogicalHeightDefinite()) {
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index 3503a81..1faf10a 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -1732,20 +1732,11 @@
   bool IsCustomItem() const;
   bool IsCustomItemShrinkToFit() const;
 
-  // TODO(dgrogan): Replace the rest of the calls to IsFlexItem with
-  // IsFlexItemIncludingNG when all the callsites can handle an item with an NG
-  // parent.
-  bool IsFlexItem() const {
-    NOT_DESTROYED();
-    return IsFlexItemCommon() && Parent()->IsFlexibleBox();
-  }
+  // TODO(1229581): Rename this function.
   bool IsFlexItemIncludingNG() const {
     NOT_DESTROYED();
-    return IsFlexItemCommon() && Parent()->IsFlexibleBoxIncludingNG();
-  }
-  bool IsFlexItemCommon() const {
-    NOT_DESTROYED();
-    return !IsInline() && !IsOutOfFlowPositioned() && Parent();
+    return !IsInline() && !IsOutOfFlowPositioned() && Parent() &&
+           Parent()->IsFlexibleBoxIncludingNG();
   }
 
   // TODO(1229581): Rename this function.
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.cc b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
index 59acb303..ce1f50f0 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
@@ -35,7 +35,6 @@
 #include "third_party/blink/renderer/core/html/html_body_element.h"
 #include "third_party/blink/renderer/core/layout/geometry/transform_state.h"
 #include "third_party/blink/renderer/core/layout/layout_block.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_object_inlines.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -128,6 +127,8 @@
 bool LayoutBoxModelObject::UsesCompositedScrolling() const {
   NOT_DESTROYED();
 
+  // TODO(crbug.com/1414885): We may need to redefine this function for
+  // CompositeScrollAfterPaint.
   const auto* properties = FirstFragment().PaintProperties();
   return properties && properties->ScrollTranslation() &&
          properties->ScrollTranslation()->HasDirectCompositingReasons();
@@ -625,12 +626,8 @@
     cb->AddPercentHeightDescendant(const_cast<LayoutBox*>(To<LayoutBox>(this)));
   }
   if (this_box && this_box->IsFlexItemIncludingNG()) {
-    if (this_box->IsFlexItem()) {
-      const auto& flex_box = To<LayoutFlexibleBox>(*Parent());
-      if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this_box))
-        return false;
-    } else if (const NGLayoutResult* result =
-                   this_box->GetSingleCachedLayoutResult()) {
+    if (const NGLayoutResult* result =
+            this_box->GetSingleCachedLayoutResult()) {
       // TODO(dgrogan): We won't get here when laying out the FlexNG item and
       // its descendant(s) for the first time because the item (|this_box|)
       // doesn't have anything in its cache. That seems bad because this method
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.h b/third_party/blink/renderer/core/layout/layout_box_model_object.h
index 5ff6b21..15e9009 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.h
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.h
@@ -533,6 +533,7 @@
   // opaque background.
   virtual bool TextIsKnownToBeOnOpaqueBackground() const {
     NOT_DESTROYED();
+    DCHECK(!RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled());
     return false;
   }
 
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
deleted file mode 100644
index 0e2fed2..0000000
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ /dev/null
@@ -1,1885 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
-
-#include <limits>
-#include "base/auto_reset.h"
-#include "third_party/blink/renderer/core/frame/web_feature.h"
-#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
-#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
-#include "third_party/blink/renderer/core/input_type_names.h"
-#include "third_party/blink/renderer/core/layout/flexible_box_algorithm.h"
-#include "third_party/blink/renderer/core/layout/layout_state.h"
-#include "third_party/blink/renderer/core/layout/layout_video.h"
-#include "third_party/blink/renderer/core/layout/layout_view.h"
-#include "third_party/blink/renderer/core/layout/min_max_sizes.h"
-#include "third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
-#include "third_party/blink/renderer/core/layout/text_autosizer.h"
-#include "third_party/blink/renderer/core/paint/block_painter.h"
-#include "third_party/blink/renderer/core/paint/paint_layer.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
-#include "third_party/blink/renderer/core/style/computed_style.h"
-#include "third_party/blink/renderer/platform/geometry/length_functions.h"
-#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
-#include "third_party/blink/renderer/platform/wtf/math_extras.h"
-
-namespace blink {
-
-static bool HasAspectRatio(const LayoutBox& child) {
-  return child.IsImage() || child.IsCanvas() || IsA<LayoutVideo>(child) ||
-         !child.StyleRef().AspectRatio().IsAuto();
-}
-
-LayoutFlexibleBox::LayoutFlexibleBox(Element* element)
-    : LayoutBlock(element),
-      order_iterator_(this),
-      number_of_in_flow_children_on_first_line_(-1),
-      has_definite_height_(SizeDefiniteness::kUnknown),
-      in_layout_(false) {
-  DCHECK(!ChildrenInline());
-}
-
-LayoutFlexibleBox::~LayoutFlexibleBox() = default;
-
-void LayoutFlexibleBox::Trace(Visitor* visitor) const {
-  visitor->Trace(intrinsic_size_along_main_axis_);
-  visitor->Trace(relaid_out_children_);
-  visitor->Trace(order_iterator_);
-  LayoutBlock::Trace(visitor);
-}
-
-bool LayoutFlexibleBox::IsChildAllowed(LayoutObject* object,
-                                       const ComputedStyle& style) const {
-  NOT_DESTROYED();
-  const auto* select = DynamicTo<HTMLSelectElement>(GetNode());
-  if (UNLIKELY(select && select->UsesMenuList())) {
-    // For a size=1 <select>, we only render the active option label through the
-    // InnerElement. We do not allow adding layout objects for options and
-    // optgroups.
-    return object->GetNode() == &select->InnerElement();
-  }
-  return LayoutBlock::IsChildAllowed(object, style);
-}
-
-MinMaxSizes LayoutFlexibleBox::ComputeIntrinsicLogicalWidths() const {
-  NOT_DESTROYED();
-  MinMaxSizes sizes;
-  sizes +=
-      BorderAndPaddingLogicalWidth() + ComputeLogicalScrollbars().InlineSum();
-
-  if (HasOverrideIntrinsicContentLogicalWidth()) {
-    sizes += OverrideIntrinsicContentLogicalWidth();
-    return sizes;
-  }
-  LayoutUnit default_inline_size = DefaultIntrinsicContentInlineSize();
-  if (default_inline_size != kIndefiniteSize) {
-    sizes.max_size += default_inline_size;
-    if (!StyleRef().LogicalWidth().IsPercentOrCalc())
-      sizes.min_size = sizes.max_size;
-    return sizes;
-  }
-  if (ShouldApplySizeContainment())
-    return sizes;
-
-  MinMaxSizes child_sizes;
-
-  // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start
-  // honoring it though until the flex shorthand stops setting it to 0. See
-  // https://bugs.webkit.org/show_bug.cgi?id=116117 and
-  // https://crbug.com/240765.
-  float previous_max_content_flex_fraction = -1;
-  int number_of_items = 0;
-  for (LayoutBox* child = FirstChildBox(); child;
-       child = child->NextSiblingBox()) {
-    if (child->IsOutOfFlowPositioned())
-      continue;
-    number_of_items++;
-
-    LayoutUnit margin = MarginIntrinsicLogicalWidthForChild(*child);
-
-    LayoutUnit min_preferred_logical_width;
-    LayoutUnit max_preferred_logical_width;
-    if (child->NeedsPreferredWidthsRecalculation())
-      child->SetIntrinsicLogicalWidthsDirty();
-    ComputeChildPreferredLogicalWidths(*child, min_preferred_logical_width,
-                                       max_preferred_logical_width);
-    DCHECK_GE(min_preferred_logical_width, LayoutUnit());
-    DCHECK_GE(max_preferred_logical_width, LayoutUnit());
-    min_preferred_logical_width += margin;
-    max_preferred_logical_width += margin;
-    if (!IsColumnFlow()) {
-      child_sizes.max_size += max_preferred_logical_width;
-      if (IsMultiline()) {
-        // For multiline, the min preferred width is if you put a break between
-        // each item.
-        child_sizes.min_size =
-            std::max(child_sizes.min_size, min_preferred_logical_width);
-      } else {
-        child_sizes.min_size += min_preferred_logical_width;
-      }
-    } else {
-      child_sizes.min_size =
-          std::max(min_preferred_logical_width, child_sizes.min_size);
-      child_sizes.max_size =
-          std::max(max_preferred_logical_width, child_sizes.max_size);
-    }
-
-    previous_max_content_flex_fraction = CountIntrinsicSizeForAlgorithmChange(
-        max_preferred_logical_width, child, previous_max_content_flex_fraction);
-  }
-
-  if (!IsColumnFlow() && number_of_items > 0) {
-    LayoutUnit gap_inline_size =
-        (number_of_items - 1) *
-        FlexLayoutAlgorithm::GapBetweenItems(
-            StyleRef(),
-            LogicalSize{ContentLogicalWidth(),
-                        AvailableLogicalHeightForPercentageComputation()});
-    child_sizes.max_size += gap_inline_size;
-    if (!IsMultiline()) {
-      child_sizes.min_size += gap_inline_size;
-    }
-  }
-
-  child_sizes.max_size = std::max(child_sizes.min_size, child_sizes.max_size);
-
-  // Due to negative margins, it is possible that we calculated a negative
-  // intrinsic width. Make sure that we never return a negative width.
-  child_sizes.min_size = std::max(LayoutUnit(), child_sizes.min_size);
-  child_sizes.max_size = std::max(LayoutUnit(), child_sizes.max_size);
-
-  sizes += child_sizes;
-  return sizes;
-}
-
-float LayoutFlexibleBox::CountIntrinsicSizeForAlgorithmChange(
-    LayoutUnit max_preferred_logical_width,
-    LayoutBox* child,
-    float previous_max_content_flex_fraction) const {
-  NOT_DESTROYED();
-  // Determine whether the new version of the intrinsic size algorithm of the
-  // flexbox spec would produce a different result than our above algorithm.
-  // The algorithm produces a different result iff the max-content flex
-  // fraction (as defined in the new algorithm) is not identical for each flex
-  // item.
-  if (IsColumnFlow())
-    return previous_max_content_flex_fraction;
-  const Length& flex_basis = child->StyleRef().FlexBasis();
-  float flex_grow = child->StyleRef().FlexGrow();
-  // A flex-basis of auto will lead to a max-content flex fraction of zero, so
-  // just like an inflexible item it would compute to a size of max-content, so
-  // we ignore it here.
-  if (flex_basis.IsAuto() || flex_grow == 0)
-    return previous_max_content_flex_fraction;
-  flex_grow = std::max(1.0f, flex_grow);
-  float max_content_flex_fraction =
-      max_preferred_logical_width.ToFloat() / flex_grow;
-  if (previous_max_content_flex_fraction != -1 &&
-      max_content_flex_fraction != previous_max_content_flex_fraction) {
-    UseCounter::Count(GetDocument(),
-                      WebFeature::kFlexboxIntrinsicSizeAlgorithmIsDifferent);
-  }
-  return max_content_flex_fraction;
-}
-
-LayoutUnit LayoutFlexibleBox::SynthesizedBaselineFromBorderBox(
-    const LayoutBox& box,
-    LineDirectionMode direction) {
-  return direction == kHorizontalLine ? box.Size().Height()
-                                      : box.Size().Width();
-}
-
-LayoutUnit LayoutFlexibleBox::BaselinePosition(FontBaseline,
-                                               bool,
-                                               LineDirectionMode direction,
-                                               LinePositionMode mode) const {
-  NOT_DESTROYED();
-  DCHECK_EQ(mode, kPositionOnContainingLine);
-  // TODO(crbug.com/1131352): input[type=range] should not use
-  // LayoutFlexibleBox. We should move out this code.
-  if (const auto* input = DynamicTo<HTMLInputElement>(GetNode())) {
-    if (input->type() == input_type_names::kRange) {
-      return SynthesizedBaselineFromBorderBox(*this, direction) +
-             MarginBefore();
-    }
-  }
-  LayoutUnit baseline = FirstLineBoxBaseline();
-  if (baseline == -1) {
-    return SynthesizedBaselineFromBorderBox(*this, direction) +
-           MarginLogicalHeight();
-  }
-
-  return BeforeMarginInLineDirection(direction) + baseline;
-}
-
-LayoutUnit LayoutFlexibleBox::FirstLineBoxBaseline() const {
-  NOT_DESTROYED();
-  if (IsWritingModeRoot() || number_of_in_flow_children_on_first_line_ <= 0 ||
-      ShouldApplyLayoutContainment())
-    return LayoutUnit(-1);
-  LayoutBox* baseline_child = nullptr;
-  int child_number = 0;
-  for (LayoutBox* child = order_iterator_.First(); child;
-       child = order_iterator_.Next()) {
-    if (child->IsOutOfFlowPositioned())
-      continue;
-    if (FlexLayoutAlgorithm::AlignmentForChild(StyleRef(), child->StyleRef()) ==
-            ItemPosition::kBaseline &&
-        !HasAutoMarginsInCrossAxis(*child)) {
-      baseline_child = child;
-      break;
-    }
-    if (!baseline_child)
-      baseline_child = child;
-
-    ++child_number;
-    if (child_number == number_of_in_flow_children_on_first_line_)
-      break;
-  }
-
-  if (!baseline_child)
-    return LayoutUnit(-1);
-
-  if (!IsColumnFlow() && !MainAxisIsInlineAxis(*baseline_child)) {
-    // TODO(cbiesinger): Should LogicalTop here be LogicalLeft?
-    return CrossAxisExtentForChild(*baseline_child) +
-           baseline_child->LogicalTop();
-  }
-  if (IsColumnFlow() && MainAxisIsInlineAxis(*baseline_child)) {
-    return MainAxisExtentForChild(*baseline_child) +
-           baseline_child->LogicalTop();
-  }
-
-  LayoutUnit baseline = baseline_child->FirstLineBoxBaseline();
-  if (baseline == -1) {
-    // FIXME: We should pass |direction| into firstLineBoxBaseline and stop
-    // bailing out if we're a writing mode root. This would also fix some
-    // cases where the flexbox is orthogonal to its container.
-    LineDirectionMode direction =
-        IsHorizontalWritingMode() ? kHorizontalLine : kVerticalLine;
-    return SynthesizedBaselineFromBorderBox(*baseline_child, direction) +
-           baseline_child->LogicalTop();
-  }
-
-  return baseline + baseline_child->LogicalTop();
-}
-
-LayoutUnit LayoutFlexibleBox::InlineBlockBaseline(
-    LineDirectionMode direction) const {
-  NOT_DESTROYED();
-  return FirstLineBoxBaseline();
-}
-
-bool LayoutFlexibleBox::HasTopOverflow() const {
-  NOT_DESTROYED();
-  if (IsHorizontalWritingMode())
-    return StyleRef().ResolvedIsColumnReverseFlexDirection();
-  return StyleRef().IsLeftToRightDirection() ==
-         StyleRef().ResolvedIsRowReverseFlexDirection();
-}
-
-bool LayoutFlexibleBox::HasLeftOverflow() const {
-  NOT_DESTROYED();
-  if (IsHorizontalWritingMode()) {
-    return StyleRef().IsLeftToRightDirection() ==
-           StyleRef().ResolvedIsRowReverseFlexDirection();
-  }
-  return (StyleRef().GetWritingMode() == WritingMode::kVerticalLr) ==
-         StyleRef().ResolvedIsColumnReverseFlexDirection();
-}
-
-void LayoutFlexibleBox::MergeAnonymousFlexItems(LayoutObject* remove_child) {
-  NOT_DESTROYED();
-  // When we remove a flex item, and the previous and next siblings of the item
-  // are text nodes wrapped in anonymous flex items, the adjacent text nodes
-  // need to be merged into the same flex item.
-  LayoutObject* prev = remove_child->PreviousSibling();
-  if (!prev || !prev->IsAnonymousBlock())
-    return;
-  LayoutObject* next = remove_child->NextSibling();
-  if (!next || !next->IsAnonymousBlock())
-    return;
-  To<LayoutBoxModelObject>(next)->MoveAllChildrenTo(
-      To<LayoutBoxModelObject>(prev));
-  To<LayoutBlockFlow>(next)->DeleteLineBoxTree();
-  next->Destroy();
-  intrinsic_size_along_main_axis_.erase(next);
-}
-
-void LayoutFlexibleBox::RemoveChild(LayoutObject* child) {
-  NOT_DESTROYED();
-  if (!DocumentBeingDestroyed() &&
-      !StyleRef().IsDeprecatedFlexboxUsingFlexLayout()) {
-    MergeAnonymousFlexItems(child);
-  }
-
-  LayoutBlock::RemoveChild(child);
-  intrinsic_size_along_main_axis_.erase(child);
-}
-
-bool LayoutFlexibleBox::HitTestChildren(
-    HitTestResult& result,
-    const HitTestLocation& hit_test_location,
-    const PhysicalOffset& accumulated_offset,
-    HitTestPhase phase) {
-  NOT_DESTROYED();
-  if (phase != HitTestPhase::kForeground)
-    return false;
-
-  PhysicalOffset scrolled_offset = accumulated_offset;
-  if (IsScrollContainer())
-    scrolled_offset -= PhysicalOffset(PixelSnappedScrolledContentOffset());
-
-  for (LayoutBox* child = LastChildBox(); child;
-       child = child->PreviousSiblingBox()) {
-    if (child->HasSelfPaintingLayer())
-      continue;
-
-    PhysicalOffset child_accumulated_offset =
-        scrolled_offset + child->PhysicalLocation(this);
-    bool child_hit = child->HitTestAllPhases(result, hit_test_location,
-                                             child_accumulated_offset);
-    if (child_hit) {
-      UpdateHitTestResult(result,
-                          hit_test_location.Point() - accumulated_offset);
-      return true;
-    }
-  }
-  return false;
-}
-
-void LayoutFlexibleBox::StyleDidChange(StyleDifference diff,
-                                       const ComputedStyle* old_style) {
-  NOT_DESTROYED();
-  LayoutBlock::StyleDidChange(diff, old_style);
-
-  if (old_style &&
-      old_style->ResolvedAlignItems(SelfAlignmentNormalBehavior())
-              .GetPosition() == ItemPosition::kStretch &&
-      diff.NeedsFullLayout()) {
-    // Flex items that were previously stretching need to be relayed out so we
-    // can compute new available cross axis space. This is only necessary for
-    // stretching since other alignment values don't change the size of the
-    // box.
-    for (LayoutBox* child = FirstChildBox(); child;
-         child = child->NextSiblingBox()) {
-      ItemPosition previous_alignment =
-          child->StyleRef()
-              .ResolvedAlignSelf(SelfAlignmentNormalBehavior(), old_style)
-              .GetPosition();
-      if (previous_alignment == ItemPosition::kStretch &&
-          previous_alignment !=
-              child->StyleRef()
-                  .ResolvedAlignSelf(SelfAlignmentNormalBehavior(), Style())
-                  .GetPosition())
-        child->SetChildNeedsLayout(kMarkOnlyThis);
-    }
-  }
-}
-
-void LayoutFlexibleBox::UpdateBlockLayout(bool relayout_children) {
-  NOT_DESTROYED();
-  DCHECK(NeedsLayout());
-
-  if (!relayout_children && SimplifiedLayout())
-    return;
-
-  relaid_out_children_.clear();
-  base::AutoReset<bool> reset1(&in_layout_, true);
-  DCHECK_EQ(has_definite_height_, SizeDefiniteness::kUnknown);
-
-  if (UpdateLogicalWidthAndColumnWidth())
-    relayout_children = true;
-
-  SubtreeLayoutScope layout_scope(*this);
-  LayoutUnit previous_height = LogicalHeight();
-  SetLogicalHeight(BorderAndPaddingLogicalHeight() +
-                   ComputeLogicalScrollbars().BlockSum());
-
-  PaintLayerScrollableArea::DelayScrollOffsetClampScope delay_clamp_scope;
-
-  {
-    TextAutosizer::LayoutScope text_autosizer_layout_scope(this, &layout_scope);
-    LayoutState state(*this);
-
-    number_of_in_flow_children_on_first_line_ = -1;
-
-    PrepareOrderIteratorAndMargins();
-
-    LayoutFlexItems(relayout_children, layout_scope);
-    if (PaintLayerScrollableArea::PreventRelayoutScope::RelayoutNeeded()) {
-      // Recompute the logical width, because children may have added or removed
-      // scrollbars.
-      UpdateLogicalWidthAndColumnWidth();
-      PaintLayerScrollableArea::FreezeScrollbarsScope freeze_scrollbars_scope;
-      PrepareOrderIteratorAndMargins();
-      LayoutFlexItems(true, layout_scope);
-      PaintLayerScrollableArea::PreventRelayoutScope::ResetRelayoutNeeded();
-    }
-
-    if (LogicalHeight() != previous_height)
-      relayout_children = true;
-
-    LayoutPositionedObjects(relayout_children || IsDocumentElement());
-
-    // FIXME: css3/flexbox/repaint-rtl-column.html seems to issue paint
-    // invalidations for more overflow than it needs to.
-    ComputeLayoutOverflow(ClientLogicalBottomAfterRepositioning());
-  }
-
-  // We have to reset this, because changes to our ancestors' style can affect
-  // this value. Also, this needs to be before we call updateAfterLayout, as
-  // that function may re-enter this one.
-  has_definite_height_ = SizeDefiniteness::kUnknown;
-
-  // Update our scroll information if we're overflow:auto/scroll/hidden now
-  // that we know if we overflow or not.
-  UpdateAfterLayout();
-
-  ClearNeedsLayout();
-}
-
-void LayoutFlexibleBox::PaintChildren(const PaintInfo& paint_info,
-                                      const PhysicalOffset&) const {
-  NOT_DESTROYED();
-  BlockPainter(*this).PaintChildrenAtomically(this->GetOrderIterator(),
-                                              paint_info);
-}
-
-void LayoutFlexibleBox::RepositionLogicalHeightDependentFlexItems(
-    FlexLayoutAlgorithm& algorithm) {
-  NOT_DESTROYED();
-  Vector<FlexLine>& line_contexts = algorithm.FlexLines();
-  LayoutUnit cross_axis_start_edge = line_contexts.empty()
-                                         ? LayoutUnit()
-                                         : line_contexts[0].cross_axis_offset_;
-  // If we have a single line flexbox, the line height is all the available
-  // space. For flex-direction: row, this means we need to use the height, so
-  // we do this after calling updateLogicalHeight.
-  if (!IsMultiline() && !line_contexts.empty()) {
-    line_contexts[0].cross_axis_extent_ = CrossAxisContentExtent();
-  }
-
-  AlignFlexLines(algorithm);
-
-  AlignChildren(algorithm);
-
-  if (StyleRef().FlexWrap() == EFlexWrap::kWrapReverse) {
-    algorithm.FlipForWrapReverse(cross_axis_start_edge,
-                                 CrossAxisContentExtent());
-    for (FlexLine& line_context : line_contexts) {
-      for (FlexItem& flex_item : line_context.line_items_) {
-        ResetAlignmentForChild(*flex_item.box_,
-                               flex_item.offset_->cross_axis_offset);
-      }
-    }
-  }
-
-  // direction:rtl + flex-direction:column means the cross-axis direction is
-  // flipped.
-  FlipForRightToLeftColumn(line_contexts);
-}
-
-DISABLE_CFI_PERF
-LayoutUnit LayoutFlexibleBox::ClientLogicalBottomAfterRepositioning() {
-  NOT_DESTROYED();
-  LayoutUnit max_child_logical_bottom;
-  for (LayoutBox* child = FirstChildBox(); child;
-       child = child->NextSiblingBox()) {
-    if (child->IsOutOfFlowPositioned())
-      continue;
-    LayoutUnit child_logical_bottom = LogicalTopForChild(*child) +
-                                      LogicalHeightForChild(*child) +
-                                      MarginAfterForChild(*child);
-    max_child_logical_bottom =
-        std::max(max_child_logical_bottom, child_logical_bottom);
-  }
-  return std::max(ClientLogicalBottom(),
-                  max_child_logical_bottom + PaddingAfter());
-}
-
-bool LayoutFlexibleBox::MainAxisIsInlineAxis(const LayoutBox& child) const {
-  NOT_DESTROYED();
-  // If we have a horizontal flow, that means the main size is the width.
-  // That's the inline size for horizontal writing modes, and the block
-  // size in vertical writing modes. For a vertical flow, main size is the
-  // height, so it's the inverse. So we need the inline size if we have a
-  // horizontal flow and horizontal writing mode, or vertical flow and vertical
-  // writing mode. Otherwise we need the block size.
-  return IsHorizontalFlow() == child.IsHorizontalWritingMode();
-}
-
-bool LayoutFlexibleBox::IsColumnFlow() const {
-  NOT_DESTROYED();
-  return StyleRef().ResolvedIsColumnFlexDirection();
-}
-
-bool LayoutFlexibleBox::IsHorizontalFlow() const {
-  NOT_DESTROYED();
-  if (IsHorizontalWritingMode())
-    return !IsColumnFlow();
-  return IsColumnFlow();
-}
-
-bool LayoutFlexibleBox::IsLeftToRightFlow() const {
-  NOT_DESTROYED();
-  if (IsColumnFlow()) {
-    return blink::IsHorizontalWritingMode(StyleRef().GetWritingMode()) ||
-           IsFlippedLinesWritingMode(StyleRef().GetWritingMode());
-  }
-  return StyleRef().IsLeftToRightDirection() ^
-         StyleRef().ResolvedIsRowReverseFlexDirection();
-}
-
-bool LayoutFlexibleBox::IsMultiline() const {
-  NOT_DESTROYED();
-  return StyleRef().FlexWrap() != EFlexWrap::kNowrap;
-}
-
-Length LayoutFlexibleBox::FlexBasisForChild(const LayoutBox& child) const {
-  NOT_DESTROYED();
-  Length flex_length = child.StyleRef().FlexBasis();
-  if (flex_length.IsAuto()) {
-    flex_length = IsHorizontalFlow() ? child.StyleRef().Width()
-                                     : child.StyleRef().Height();
-  }
-  return flex_length;
-}
-
-LayoutUnit LayoutFlexibleBox::CrossAxisExtentForChild(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  return IsHorizontalFlow() ? child.Size().Height() : child.Size().Width();
-}
-
-LayoutUnit LayoutFlexibleBox::ChildUnstretchedLogicalHeight(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  // This should only be called if the logical height is the cross size
-  DCHECK(MainAxisIsInlineAxis(child));
-  if (NeedToStretchChildLogicalHeight(child)) {
-    AutoClearOverrideLogicalHeight clear(const_cast<LayoutBox*>(&child));
-
-    LayoutUnit child_intrinsic_content_logical_height;
-    // If we have size containment specified, and are not overriding the
-    // intrinsic content height, then the height is LayoutUnit(). In all other
-    // cases, this if-condition will pass and set the intrinsic height.
-    if (!child.ShouldApplySizeContainment() ||
-        child.HasOverrideIntrinsicContentLogicalHeight()) {
-      child_intrinsic_content_logical_height =
-          child.IntrinsicContentLogicalHeight();
-    }
-
-    LayoutUnit child_intrinsic_logical_height =
-        child_intrinsic_content_logical_height +
-        child.ComputeLogicalScrollbars().BlockSum() +
-        child.BorderAndPaddingLogicalHeight();
-    LogicalExtentComputedValues values;
-    child.ComputeLogicalHeight(child_intrinsic_logical_height, LayoutUnit(),
-                               values);
-    return values.extent_;
-  }
-  return child.LogicalHeight();
-}
-
-DISABLE_CFI_PERF
-LayoutUnit LayoutFlexibleBox::ChildUnstretchedLogicalWidth(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  // This should only be called if the logical width is the cross size
-  DCHECK(!MainAxisIsInlineAxis(child));
-
-  // We compute the width as if we were unstretched. Only the main axis
-  // override size is set at this point.
-  // However, if our cross axis length is definite we don't need to recompute
-  // and can just return the already-set logical width.
-  if (!CrossAxisLengthIsDefinite(child, child.StyleRef().LogicalWidth())) {
-    AutoClearOverrideLogicalWidth clear(const_cast<LayoutBox*>(&child));
-
-    LogicalExtentComputedValues values;
-    child.ComputeLogicalWidth(values);
-    return values.extent_;
-  }
-
-  return child.LogicalWidth();
-}
-
-LayoutUnit LayoutFlexibleBox::CrossAxisUnstretchedExtentForChild(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  return MainAxisIsInlineAxis(child) ? ChildUnstretchedLogicalHeight(child)
-                                     : ChildUnstretchedLogicalWidth(child);
-}
-
-LayoutUnit LayoutFlexibleBox::MainAxisExtentForChild(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  return IsHorizontalFlow() ? child.Size().Width() : child.Size().Height();
-}
-
-LayoutUnit LayoutFlexibleBox::MainAxisContentExtentForChild(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  return IsHorizontalFlow() ? child.ContentWidth() : child.ContentHeight();
-}
-
-LayoutUnit LayoutFlexibleBox::MainAxisContentExtentForChildIncludingScrollbar(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  return IsHorizontalFlow()
-             ? child.ContentWidth() + child.ComputeScrollbars().HorizontalSum()
-             : child.ContentHeight() + child.ComputeScrollbars().VerticalSum();
-}
-
-LayoutUnit LayoutFlexibleBox::CrossAxisExtent() const {
-  NOT_DESTROYED();
-  return IsHorizontalFlow() ? Size().Height() : Size().Width();
-}
-
-LayoutUnit LayoutFlexibleBox::CrossAxisContentExtent() const {
-  NOT_DESTROYED();
-  return IsHorizontalFlow() ? ContentHeight() : ContentWidth();
-}
-
-LayoutUnit LayoutFlexibleBox::MainAxisContentExtent(
-    LayoutUnit content_logical_height) {
-  NOT_DESTROYED();
-  if (IsColumnFlow()) {
-    LogicalExtentComputedValues computed_values;
-    LayoutUnit border_padding_and_scrollbar =
-        BorderAndPaddingLogicalHeight() + ComputeLogicalScrollbars().BlockSum();
-    LayoutUnit border_box_logical_height =
-        content_logical_height + border_padding_and_scrollbar;
-    ComputeLogicalHeight(border_box_logical_height, LogicalTop(),
-                         computed_values);
-    if (computed_values.extent_ == LayoutUnit::Max())
-      return computed_values.extent_;
-    return std::max(LayoutUnit(),
-                    computed_values.extent_ - border_padding_and_scrollbar);
-  }
-  return ContentLogicalWidth();
-}
-
-LayoutUnit LayoutFlexibleBox::ComputeMainAxisExtentForChild(
-    const LayoutBox& child,
-    SizeType size_type,
-    const Length& size,
-    LayoutUnit border_and_padding) const {
-  NOT_DESTROYED();
-  if (!MainAxisIsInlineAxis(child)) {
-    // We don't have to check for "auto" here - computeContentLogicalHeight
-    // will just return -1 for that case anyway. It's safe to access
-    // scrollbarLogicalHeight here because ComputeNextFlexLine will have
-    // already forced layout on the child. We previously layed out the child
-    // if necessary (see ComputeNextFlexLine and the call to
-    // childHasIntrinsicMainAxisSize) so we can be sure that the two height
-    // calls here will return up-to-date data.
-    LayoutUnit logical_height = child.ComputeContentLogicalHeight(
-        size_type, size, child.IntrinsicContentLogicalHeight());
-    if (logical_height == -1)
-      return logical_height;
-    return logical_height + child.ComputeLogicalScrollbars().BlockSum();
-  }
-  // computeLogicalWidth always re-computes the intrinsic widths. However, when
-  // our logical width is auto, we can just use our cached value. So let's do
-  // that here. (Compare code in LayoutBlock::computePreferredLogicalWidths)
-  if (child.StyleRef().LogicalWidth().IsAuto() && !HasAspectRatio(child)) {
-    if (size.IsMinContent() || size.IsMinIntrinsic())
-      return child.PreferredLogicalWidths().min_size - border_and_padding;
-    if (size.IsMaxContent())
-      return child.PreferredLogicalWidths().max_size - border_and_padding;
-  }
-  return child.ComputeLogicalWidthUsing(size_type, size, ContentLogicalWidth(),
-                                        this) -
-         border_and_padding;
-}
-
-LayoutUnit LayoutFlexibleBox::ContentInsetRight() const {
-  NOT_DESTROYED();
-  return BorderRight() + PaddingRight() + ComputeScrollbars().right;
-}
-
-LayoutUnit LayoutFlexibleBox::ContentInsetBottom() const {
-  NOT_DESTROYED();
-  return BorderBottom() + PaddingBottom() + ComputeScrollbars().bottom;
-}
-
-LayoutUnit LayoutFlexibleBox::FlowAwareContentInsetStart() const {
-  NOT_DESTROYED();
-  if (IsHorizontalFlow())
-    return IsLeftToRightFlow() ? ContentLeft() : ContentInsetRight();
-  return IsLeftToRightFlow() ? ContentTop() : ContentInsetBottom();
-}
-
-LayoutUnit LayoutFlexibleBox::FlowAwareContentInsetEnd() const {
-  NOT_DESTROYED();
-  if (IsHorizontalFlow())
-    return IsLeftToRightFlow() ? ContentInsetRight() : ContentLeft();
-  return IsLeftToRightFlow() ? ContentInsetBottom() : ContentTop();
-}
-
-LayoutUnit LayoutFlexibleBox::FlowAwareContentInsetBefore() const {
-  NOT_DESTROYED();
-  switch (FlexLayoutAlgorithm::GetTransformedWritingMode(StyleRef())) {
-    case TransformedWritingMode::kTopToBottomWritingMode:
-      return ContentTop();
-    case TransformedWritingMode::kBottomToTopWritingMode:
-      return ContentInsetBottom();
-    case TransformedWritingMode::kLeftToRightWritingMode:
-      return ContentLeft();
-    case TransformedWritingMode::kRightToLeftWritingMode:
-      return ContentInsetRight();
-  }
-  NOTREACHED();
-}
-
-DISABLE_CFI_PERF
-LayoutUnit LayoutFlexibleBox::FlowAwareContentInsetAfter() const {
-  NOT_DESTROYED();
-  switch (FlexLayoutAlgorithm::GetTransformedWritingMode(StyleRef())) {
-    case TransformedWritingMode::kTopToBottomWritingMode:
-      return ContentInsetBottom();
-    case TransformedWritingMode::kBottomToTopWritingMode:
-      return ContentTop();
-    case TransformedWritingMode::kLeftToRightWritingMode:
-      return ContentInsetRight();
-    case TransformedWritingMode::kRightToLeftWritingMode:
-      return ContentLeft();
-  }
-  NOTREACHED();
-}
-
-LayoutUnit LayoutFlexibleBox::CrossAxisScrollbarExtent() const {
-  NOT_DESTROYED();
-  return IsHorizontalFlow() ? ComputeScrollbars().HorizontalSum()
-                            : ComputeScrollbars().VerticalSum();
-}
-
-LayoutUnit LayoutFlexibleBox::CrossAxisScrollbarExtentForChild(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  return IsHorizontalFlow() ? child.ComputeScrollbars().HorizontalSum()
-                            : child.ComputeScrollbars().VerticalSum();
-}
-
-FlexOffset LayoutFlexibleBox::FlowAwareLocationForChild(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  LayoutPoint location = IsHorizontalFlow()
-                             ? child.Location()
-                             : child.Location().TransposedPoint();
-  return FlexOffset(location.X(), location.Y());
-}
-
-bool LayoutFlexibleBox::UseChildAspectRatio(const LayoutBox& child) const {
-  NOT_DESTROYED();
-  if (!HasAspectRatio(child))
-    return false;
-  if (child.StyleRef().AspectRatio().IsAuto() &&
-      child.IntrinsicSize().Height() == 0) {
-    // We can't compute a ratio in this case.
-    return false;
-  }
-  const Length& cross_size =
-      IsHorizontalFlow() ? child.StyleRef().Height() : child.StyleRef().Width();
-  return CrossAxisLengthIsDefinite(child, cross_size);
-}
-
-LayoutUnit LayoutFlexibleBox::ComputeMainSizeFromAspectRatioUsing(
-    const LayoutBox& child,
-    const Length& cross_size_length,
-    LayoutUnit main_axis_border_and_padding,
-    LayoutUnit cross_axis_border_and_padding) const {
-  NOT_DESTROYED();
-  DCHECK(HasAspectRatio(child));
-
-  LayoutUnit cross_size;
-  if (cross_size_length.IsFixed()) {
-    cross_size = LayoutUnit(cross_size_length.Value());
-  } else {
-    DCHECK(cross_size_length.IsPercentOrCalc());
-    cross_size = MainAxisIsInlineAxis(child)
-                     ? child.ComputePercentageLogicalHeight(cross_size_length)
-                     : AdjustBorderBoxLogicalWidthForBoxSizing(
-                           ValueForLength(cross_size_length, ContentWidth()));
-  }
-
-  LayoutSize aspect_ratio = child.IntrinsicSize();
-  EAspectRatioType ar_type = child.StyleRef().AspectRatio().GetType();
-  LayoutUnit border_and_padding;
-  if (ar_type == EAspectRatioType::kRatio ||
-      (ar_type == EAspectRatioType::kAutoAndRatio && aspect_ratio.IsEmpty())) {
-    aspect_ratio = LayoutSize(child.StyleRef().AspectRatio().GetRatio());
-    if (child.StyleRef().BoxSizingForAspectRatio() == EBoxSizing::kContentBox) {
-      cross_size -= cross_axis_border_and_padding;
-      border_and_padding = main_axis_border_and_padding;
-    }
-  }
-  // TODO(cbiesinger): box sizing?
-  double ratio =
-      aspect_ratio.Width().ToFloat() / aspect_ratio.Height().ToFloat();
-  if (IsHorizontalFlow())
-    return LayoutUnit(cross_size * ratio) + border_and_padding;
-  return LayoutUnit(cross_size / ratio) + border_and_padding;
-}
-
-void LayoutFlexibleBox::SetFlowAwareLocationForChild(
-    LayoutBox& child,
-    const FlexOffset& flex_offset) {
-  NOT_DESTROYED();
-  if (IsHorizontalFlow()) {
-    child.SetLocationAndUpdateOverflowControlsIfNeeded(LayoutPoint(
-        flex_offset.main_axis_offset, flex_offset.cross_axis_offset));
-  } else {
-    child.SetLocationAndUpdateOverflowControlsIfNeeded(LayoutPoint(
-        flex_offset.cross_axis_offset, flex_offset.main_axis_offset));
-  }
-}
-
-bool LayoutFlexibleBox::MainAxisLengthIsDefinite(const LayoutBox& child,
-                                                 const Length& flex_basis,
-                                                 bool add_to_cb) const {
-  NOT_DESTROYED();
-  // 'content' isn't actually supported in legacy flex. Checking IsContent() and
-  // returning false on the next line prevents a DCHECK though.
-  if (flex_basis.IsAuto() || flex_basis.IsContent())
-    return false;
-  if (IsColumnFlow() && flex_basis.IsContentOrIntrinsicOrFillAvailable())
-    return false;
-  if (flex_basis.IsPercentOrCalc()) {
-    if (!IsColumnFlow() || has_definite_height_ == SizeDefiniteness::kDefinite)
-      return true;
-    if (has_definite_height_ == SizeDefiniteness::kIndefinite)
-      return false;
-    if (child.HasOverrideContainingBlockContentLogicalHeight()) {
-      // We don't want to cache this. To be a bit more efficient, just check
-      // whether the override height is -1 or not and return the value based on
-      // that.
-      DCHECK(!add_to_cb);
-      LayoutUnit override_height =
-          child.OverrideContainingBlockContentLogicalHeight();
-      return override_height == LayoutUnit(-1) ? false : true;
-    }
-    LayoutBlock* cb = nullptr;
-    bool definite =
-        child.ContainingBlockLogicalHeightForPercentageResolution(&cb) != -1;
-    if (add_to_cb)
-      cb->AddPercentHeightDescendant(const_cast<LayoutBox*>(&child));
-    if (in_layout_) {
-      // We can reach this code even while we're not laying ourselves out, such
-      // as from mainSizeForPercentageResolution.
-      has_definite_height_ = definite ? SizeDefiniteness::kDefinite
-                                      : SizeDefiniteness::kIndefinite;
-    }
-    return definite;
-  }
-  return true;
-}
-
-bool LayoutFlexibleBox::CrossAxisLengthIsDefinite(const LayoutBox& child,
-                                                  const Length& length) const {
-  NOT_DESTROYED();
-  if (length.IsAuto())
-    return false;
-  if (length.IsPercentOrCalc()) {
-    if (!MainAxisIsInlineAxis(child) ||
-        has_definite_height_ == SizeDefiniteness::kDefinite)
-      return true;
-    if (has_definite_height_ == SizeDefiniteness::kIndefinite)
-      return false;
-    bool definite =
-        child.ContainingBlockLogicalHeightForPercentageResolution() != -1;
-    has_definite_height_ =
-        definite ? SizeDefiniteness::kDefinite : SizeDefiniteness::kIndefinite;
-    return definite;
-  }
-  // TODO(cbiesinger): Eventually we should support other types of sizes here.
-  // Requires updating computeMainSizeFromAspectRatioUsing.
-  return length.IsFixed();
-}
-
-void LayoutFlexibleBox::CacheChildMainSize(const LayoutBox& child) {
-  NOT_DESTROYED();
-  DCHECK(!child.SelfNeedsLayout());
-  DCHECK(!child.NeedsLayout() || child.ChildLayoutBlockedByDisplayLock());
-  LayoutUnit main_size;
-  if (MainAxisIsInlineAxis(child)) {
-    main_size = child.PreferredLogicalWidths().max_size;
-  } else {
-    if (FlexBasisForChild(child).IsPercentOrCalc() &&
-        !MainAxisLengthIsDefinite(child, FlexBasisForChild(child))) {
-      main_size = child.IntrinsicContentLogicalHeight() +
-                  child.BorderAndPaddingLogicalHeight() +
-                  child.ComputeLogicalScrollbars().BlockSum();
-    } else {
-      main_size = child.LogicalHeight();
-    }
-  }
-  intrinsic_size_along_main_axis_.Set(&child, main_size);
-  relaid_out_children_.insert(&child);
-}
-
-void LayoutFlexibleBox::ClearCachedMainSizeForChild(const LayoutBox& child) {
-  NOT_DESTROYED();
-  intrinsic_size_along_main_axis_.erase(&child);
-}
-
-bool LayoutFlexibleBox::CanAvoidLayoutForNGChild(const LayoutBox& child) const {
-  NOT_DESTROYED();
-  if (!child.IsLayoutNGObject())
-    return false;
-
-  // If the last layout was done with a different override size, or different
-  // definite-ness, we need to force-relayout so that percentage sizes are
-  // resolved correctly.
-  const NGLayoutResult* cached_layout_result =
-      child.GetSingleCachedLayoutResult();
-  if (!cached_layout_result)
-    return false;
-
-  const NGConstraintSpace& old_space =
-      cached_layout_result->GetConstraintSpaceForCaching();
-  if (old_space.IsFixedInlineSize() != child.HasOverrideLogicalWidth())
-    return false;
-  if (old_space.IsFixedBlockSize() != child.HasOverrideLogicalHeight())
-    return false;
-  if (!old_space.IsInitialBlockSizeIndefinite() !=
-      UseOverrideLogicalHeightForPerentageResolution(child))
-    return false;
-  if (child.HasOverrideLogicalWidth() &&
-      old_space.AvailableSize().inline_size != child.OverrideLogicalWidth())
-    return false;
-  if (child.HasOverrideLogicalHeight() &&
-      old_space.AvailableSize().block_size != child.OverrideLogicalHeight())
-    return false;
-  return true;
-}
-
-DISABLE_CFI_PERF
-LayoutUnit LayoutFlexibleBox::ComputeInnerFlexBaseSizeForChild(
-    LayoutBox& child,
-    LayoutUnit main_axis_border_and_padding,
-    LayoutUnit cross_axis_border_and_padding,
-    ChildLayoutType child_layout_type) {
-  NOT_DESTROYED();
-  if (child.IsImage() || IsA<LayoutVideo>(child) || child.IsCanvas())
-    UseCounter::Count(GetDocument(), WebFeature::kAspectRatioFlexItem);
-
-  Length flex_basis = FlexBasisForChild(child);
-  // -webkit-box sizes as fit-content instead of max-content.
-  if (flex_basis.IsAuto() &&
-      (StyleRef().IsDeprecatedWebkitBox() &&
-       (StyleRef().BoxOrient() == EBoxOrient::kHorizontal ||
-        StyleRef().BoxAlign() != EBoxAlignment::kStretch))) {
-    flex_basis = Length(Length::kFitContent);
-  }
-  if (MainAxisLengthIsDefinite(child, flex_basis)) {
-    return std::max(LayoutUnit(), ComputeMainAxisExtentForChild(
-                                      child, kMainOrPreferredSize, flex_basis,
-                                      main_axis_border_and_padding));
-  }
-
-  if (UseChildAspectRatio(child)) {
-    const Length& cross_size_length = IsHorizontalFlow()
-                                          ? child.StyleRef().Height()
-                                          : child.StyleRef().Width();
-    LayoutUnit result = ComputeMainSizeFromAspectRatioUsing(
-        child, cross_size_length, main_axis_border_and_padding,
-        cross_axis_border_and_padding);
-    result = AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
-        child, result, main_axis_border_and_padding,
-        cross_axis_border_and_padding);
-    return result - main_axis_border_and_padding;
-  }
-
-  // The flex basis is indefinite (=auto), so we need to compute the actual
-  // width of the child. For the logical width axis we just use the preferred
-  // width; for the height we need to lay out the child.
-  LayoutUnit main_axis_extent;
-  if (MainAxisIsInlineAxis(child)) {
-    // We don't need to add ComputeLogicalScrollbars().InlineSum() here because
-    // the preferred width includes the scrollbar, even for overflow: auto.
-    main_axis_extent = child.PreferredLogicalWidths().max_size;
-  } else {
-    // The needed value here is the logical height. This value does not include
-    // the border/scrollbar/padding size, so we have to add the scrollbar.
-    if (child.HasOverrideIntrinsicContentLogicalHeight()) {
-      return child.OverrideIntrinsicContentLogicalHeight() +
-             LayoutUnit(child.ComputeLogicalScrollbars().BlockSum());
-    }
-    if (child.ShouldApplySizeContainment())
-      return LayoutUnit(child.ComputeLogicalScrollbars().BlockSum());
-
-    if (child_layout_type == kNeverLayout)
-      return LayoutUnit();
-
-    DCHECK(!child.NeedsLayout());
-    DCHECK(intrinsic_size_along_main_axis_.Contains(&child));
-    main_axis_extent = intrinsic_size_along_main_axis_.at(&child);
-  }
-  DCHECK_GE(main_axis_extent - main_axis_border_and_padding, LayoutUnit())
-      << main_axis_extent << " - " << main_axis_border_and_padding;
-  return main_axis_extent - main_axis_border_and_padding;
-}
-
-void LayoutFlexibleBox::LayoutFlexItems(bool relayout_children,
-                                        SubtreeLayoutScope& layout_scope) {
-  NOT_DESTROYED();
-  PaintLayerScrollableArea::PreventRelayoutScope prevent_relayout_scope(
-      layout_scope);
-
-  // Set up our list of flex items. All of the rest of the algorithm should
-  // work off this list of a subset.
-  ChildLayoutType layout_type =
-      relayout_children ? kForceLayout : kLayoutIfNeeded;
-  const LayoutUnit line_break_length = MainAxisContentExtent(LayoutUnit::Max());
-  FlexLayoutAlgorithm flex_algorithm(
-      Style(), line_break_length,
-      LogicalSize{ContentLogicalWidth(),
-                  AvailableLogicalHeightForPercentageComputation()},
-      &GetDocument());
-  order_iterator_.First();
-  for (LayoutBox* child = order_iterator_.CurrentChild(); child;
-       child = order_iterator_.Next()) {
-    if (child->IsOutOfFlowPositioned()) {
-      // Out-of-flow children are not flex items, so we skip them here.
-      PrepareChildForPositionedLayout(*child);
-      continue;
-    }
-
-    ConstructAndAppendFlexItem(&flex_algorithm, *child, layout_type);
-  }
-  // Because we set the override containing block logical height to -1 in
-  // ConstructAndAppendFlexItem, any value we may have cached for definiteness
-  // is incorrect; just reset it here.
-  has_definite_height_ = SizeDefiniteness::kUnknown;
-
-  LayoutUnit cross_axis_offset = FlowAwareContentInsetBefore();
-  LayoutUnit logical_width = LogicalWidth();
-  FlexLine* current_line;
-  Vector<FlexOffset> item_offsets(flex_algorithm.NumItems());
-  FlexOffset* current_item_offset = item_offsets.begin();
-  while ((current_line = flex_algorithm.ComputeNextFlexLine(logical_width))) {
-    DCHECK_GE(current_line->line_items_.size(), 0ULL);
-    current_line->SetContainerMainInnerSize(
-        MainAxisContentExtent(current_line->sum_hypothetical_main_size_));
-    current_line->FreezeInflexibleItems();
-
-    while (!current_line->ResolveFlexibleLengths()) {
-      DCHECK_GE(current_line->total_flex_grow_, 0);
-      DCHECK_GE(current_line->total_weighted_flex_shrink_, 0);
-    }
-
-    LayoutLineItems(current_line, relayout_children, layout_scope,
-                    &current_item_offset);
-
-    current_line->ComputeLineItemsPosition(FlowAwareContentInsetStart(),
-                                           FlowAwareContentInsetEnd(),
-                                           cross_axis_offset);
-    ApplyLineItemsPosition(current_line);
-    if (number_of_in_flow_children_on_first_line_ == -1) {
-      number_of_in_flow_children_on_first_line_ =
-          current_line->line_items_.size();
-    }
-  }
-  if (HasLineIfEmpty()) {
-    // Even if ComputeNextFlexLine returns true, the flexbox might not have
-    // a line because all our children might be out of flow positioned.
-    // Instead of just checking if we have a line, make sure the flexbox
-    // has at least a line's worth of height to cover this case.
-    LayoutUnit min_height = MinimumLogicalHeightForEmptyLine();
-    if (Size().Height() < min_height)
-      SetLogicalHeight(min_height);
-  }
-  if (!IsColumnFlow()) {
-    SetLogicalHeight(LogicalHeight() +
-                     flex_algorithm.gap_between_lines_ *
-                         (flex_algorithm.FlexLines().size() - 1));
-  }
-  UpdateLogicalHeight();
-  if (!HasOverrideLogicalHeight() && IsColumnFlow()) {
-    SetIntrinsicContentLogicalHeight(
-        flex_algorithm.IntrinsicContentBlockSize());
-  }
-  RepositionLogicalHeightDependentFlexItems(flex_algorithm);
-}
-
-bool LayoutFlexibleBox::HasAutoMarginsInCrossAxis(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  if (IsHorizontalFlow()) {
-    return child.StyleRef().MarginTop().IsAuto() ||
-           child.StyleRef().MarginBottom().IsAuto();
-  }
-  return child.StyleRef().MarginLeft().IsAuto() ||
-         child.StyleRef().MarginRight().IsAuto();
-}
-
-LayoutUnit LayoutFlexibleBox::ComputeChildMarginValue(const Length& margin) {
-  NOT_DESTROYED();
-  // When resolving the margins, we use the content size for resolving percent
-  // and calc (for percents in calc expressions) margins. Fortunately, percent
-  // margins are always computed with respect to the block's width, even for
-  // margin-top and margin-bottom.
-  LayoutUnit available_size = ContentLogicalWidth();
-  return MinimumValueForLength(margin, available_size);
-}
-
-void LayoutFlexibleBox::PrepareOrderIteratorAndMargins() {
-  NOT_DESTROYED();
-  OrderIteratorPopulator populator(order_iterator_);
-
-  for (LayoutBox* child = FirstChildBox(); child;
-       child = child->NextSiblingBox()) {
-    populator.CollectChild(child);
-
-    if (child->IsOutOfFlowPositioned())
-      continue;
-
-    // Before running the flex algorithm, 'auto' has a margin of 0.
-    const ComputedStyle& style = child->StyleRef();
-    child->SetMarginTop(ComputeChildMarginValue(style.MarginTop()));
-    child->SetMarginRight(ComputeChildMarginValue(style.MarginRight()));
-    child->SetMarginBottom(ComputeChildMarginValue(style.MarginBottom()));
-    child->SetMarginLeft(ComputeChildMarginValue(style.MarginLeft()));
-  }
-}
-
-DISABLE_CFI_PERF
-MinMaxSizes LayoutFlexibleBox::ComputeMinAndMaxSizesForChild(
-    const FlexLayoutAlgorithm& algorithm,
-    const LayoutBox& child,
-    LayoutUnit border_and_padding,
-    LayoutUnit cross_axis_border_and_padding) const {
-  NOT_DESTROYED();
-  MinMaxSizes sizes{LayoutUnit(), LayoutUnit::Max()};
-
-  const Length& max = IsHorizontalFlow() ? child.StyleRef().MaxWidth()
-                                         : child.StyleRef().MaxHeight();
-  if (!max.IsNone()) {
-    sizes.max_size =
-        ComputeMainAxisExtentForChild(child, kMaxSize, max, border_and_padding);
-    if (sizes.max_size == -1)
-      sizes.max_size = LayoutUnit::Max();
-    DCHECK_GE(sizes.max_size, LayoutUnit());
-  }
-
-  const Length& min = IsHorizontalFlow() ? child.StyleRef().MinWidth()
-                                         : child.StyleRef().MinHeight();
-  if (!min.IsAuto()) {
-    sizes.min_size =
-        ComputeMainAxisExtentForChild(child, kMinSize, min, border_and_padding);
-    // computeMainAxisExtentForChild can return -1 when the child has a
-    // percentage min size, but we have an indefinite size in that axis.
-    sizes.min_size = std::max(LayoutUnit(), sizes.min_size);
-  } else if (algorithm.ShouldApplyMinSizeAutoForChild(child)) {
-    LayoutUnit content_size = ComputeMainAxisExtentForChild(
-        child, kMinSize, Length::MinContent(), border_and_padding);
-    DCHECK_GE(content_size, LayoutUnit());
-    if (HasAspectRatio(child) && child.IntrinsicSize().Height() > 0) {
-      content_size = AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
-          child, content_size, border_and_padding,
-          cross_axis_border_and_padding);
-    }
-    if (child.IsTable() && !IsColumnFlow()) {
-      // Avoid resolving minimum size to something narrower than the minimum
-      // preferred logical width of the table.
-      sizes.min_size = content_size;
-    } else {
-      if (sizes.max_size != -1 && content_size > sizes.max_size)
-        content_size = sizes.max_size;
-
-      const Length& main_size = IsHorizontalFlow() ? child.StyleRef().Width()
-                                                   : child.StyleRef().Height();
-      if (MainAxisLengthIsDefinite(child, main_size)) {
-        LayoutUnit resolved_main_size = ComputeMainAxisExtentForChild(
-            child, kMainOrPreferredSize, main_size, border_and_padding);
-        DCHECK_GE(resolved_main_size, LayoutUnit());
-        LayoutUnit specified_size =
-            sizes.max_size != -1 ? std::min(resolved_main_size, sizes.max_size)
-                                 : resolved_main_size;
-
-        sizes.min_size = std::min(specified_size, content_size);
-      } else if (child.IsLayoutReplaced() && UseChildAspectRatio(child)) {
-        const Length& cross_size_length = IsHorizontalFlow()
-                                              ? child.StyleRef().Height()
-                                              : child.StyleRef().Width();
-        LayoutUnit transferred_size = ComputeMainSizeFromAspectRatioUsing(
-            child, cross_size_length, border_and_padding,
-            cross_axis_border_and_padding);
-        transferred_size = AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
-            child, transferred_size, border_and_padding,
-            cross_axis_border_and_padding);
-        sizes.min_size = std::min(transferred_size, content_size);
-      } else {
-        sizes.min_size = content_size;
-      }
-    }
-  }
-  DCHECK_GE(sizes.min_size, LayoutUnit());
-  return sizes;
-}
-
-bool LayoutFlexibleBox::CrossSizeIsDefiniteForPercentageResolution(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  DCHECK(MainAxisIsInlineAxis(child));
-  if (FlexLayoutAlgorithm::AlignmentForChild(StyleRef(), child.StyleRef()) !=
-      ItemPosition::kStretch)
-    return false;
-
-  // Here we implement https://drafts.csswg.org/css-flexbox/#algo-stretch
-  if (child.HasOverrideLogicalHeight())
-    return true;
-
-  // We don't currently implement the optimization from
-  // https://drafts.csswg.org/css-flexbox/#definite-sizes case 1. While that
-  // could speed up a specialized case, it requires determining if we have a
-  // definite size, which itself is not cheap. We can consider implementing it
-  // at a later time. (The correctness is ensured by redoing layout in
-  // applyStretchAlignmentToChild)
-  return false;
-}
-
-bool LayoutFlexibleBox::MainSizeIsDefiniteForPercentageResolution(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  DCHECK(!MainAxisIsInlineAxis(child));
-  // This function implements section 9.8. Definite and Indefinite Sizes, case
-  // 2) of the flexbox spec.
-  // We need to check for the flexbox to have a definite main size.
-  // We make up a percentage to check whether we have a definite size.
-  if (!MainAxisLengthIsDefinite(child, Length::Percent(0), false))
-    return false;
-
-  return child.HasOverrideLogicalHeight();
-}
-
-bool LayoutFlexibleBox::UseOverrideLogicalHeightForPerentageResolution(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  if (MainAxisIsInlineAxis(child))
-    return CrossSizeIsDefiniteForPercentageResolution(child);
-  return MainSizeIsDefiniteForPercentageResolution(child);
-}
-
-LayoutUnit LayoutFlexibleBox::AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
-    const LayoutBox& child,
-    LayoutUnit child_size,
-    LayoutUnit main_axis_border_and_padding,
-    LayoutUnit cross_axis_border_and_padding) const {
-  NOT_DESTROYED();
-  const Length& cross_min = IsHorizontalFlow() ? child.StyleRef().MinHeight()
-                                               : child.StyleRef().MinWidth();
-  const Length& cross_max = IsHorizontalFlow() ? child.StyleRef().MaxHeight()
-                                               : child.StyleRef().MaxWidth();
-
-  if (CrossAxisLengthIsDefinite(child, cross_max)) {
-    LayoutUnit max_value = ComputeMainSizeFromAspectRatioUsing(
-        child, cross_max, main_axis_border_and_padding,
-        cross_axis_border_and_padding);
-    child_size = std::min(max_value, child_size);
-  }
-
-  if (CrossAxisLengthIsDefinite(child, cross_min)) {
-    LayoutUnit min_value = ComputeMainSizeFromAspectRatioUsing(
-        child, cross_min, main_axis_border_and_padding,
-        cross_axis_border_and_padding);
-    child_size = std::max(min_value, child_size);
-  }
-
-  return child_size;
-}
-
-DISABLE_CFI_PERF
-void LayoutFlexibleBox::ConstructAndAppendFlexItem(
-    FlexLayoutAlgorithm* algorithm,
-    LayoutBox& child,
-    ChildLayoutType layout_type) {
-  NOT_DESTROYED();
-  if (layout_type != kNeverLayout &&
-      ChildHasIntrinsicMainAxisSize(*algorithm, child)) {
-    // If this condition is true, then ComputeMainAxisExtentForChild will call
-    // child.IntrinsicContentLogicalHeight() and
-    // child.ScrollbarLogicalHeight(), so if the child has intrinsic
-    // min/max/preferred size, run layout on it now to make sure its logical
-    // height and scroll bars are up to date.
-    // For column flow flex containers, we even need to do this for children
-    // that don't need layout, if there's a chance that the logical width of
-    // the flex container has changed (because that may affect the intrinsic
-    // height of the child).
-    UpdateBlockChildDirtyBitsBeforeLayout(layout_type == kForceLayout, child);
-    if (child.NeedsLayout() || layout_type == kForceLayout ||
-        !intrinsic_size_along_main_axis_.Contains(&child)) {
-      // Don't resolve percentages in children. This is especially important
-      // for the min-height calculation, where we want percentages to be
-      // treated as auto. For flex-basis itself, this is not a problem because
-      // by definition we have an indefinite flex basis here and thus
-      // percentages should not resolve.
-      if (IsHorizontalWritingMode() == child.IsHorizontalWritingMode())
-        child.SetOverrideContainingBlockContentLogicalHeight(LayoutUnit(-1));
-      else
-        child.SetOverrideContainingBlockContentLogicalWidth(LayoutUnit(-1));
-      child.ClearOverrideSize();
-      child.ForceLayout();
-      CacheChildMainSize(child);
-      child.ClearOverrideContainingBlockContentSize();
-    }
-  }
-
-  LayoutUnit main_axis_border_padding = IsHorizontalFlow()
-                                            ? child.BorderAndPaddingWidth()
-                                            : child.BorderAndPaddingHeight();
-  LayoutUnit cross_axis_border_padding = IsHorizontalFlow()
-                                             ? child.BorderAndPaddingHeight()
-                                             : child.BorderAndPaddingWidth();
-
-  LayoutUnit child_inner_flex_base_size = ComputeInnerFlexBaseSizeForChild(
-      child, main_axis_border_padding, cross_axis_border_padding, layout_type);
-
-  MinMaxSizes sizes = ComputeMinAndMaxSizesForChild(
-      *algorithm, child, main_axis_border_padding, cross_axis_border_padding);
-
-  NGPhysicalBoxStrut physical_margins(child.MarginTop(), child.MarginRight(),
-                                      child.MarginBottom(), child.MarginLeft());
-  algorithm->emplace_back(
-      &child, child.StyleRef(), child_inner_flex_base_size, sizes,
-      /* min_max_cross_sizes */ absl::nullopt, main_axis_border_padding,
-      cross_axis_border_padding, physical_margins, /* unused */ NGBoxStrut(),
-      StyleRef().GetWritingMode());
-}
-
-void LayoutFlexibleBox::SetOverrideMainAxisContentSizeForChild(FlexItem& item) {
-  NOT_DESTROYED();
-  if (MainAxisIsInlineAxis(*item.box_)) {
-    item.box_->SetOverrideLogicalWidth(item.FlexedBorderBoxSize());
-  } else {
-    item.box_->SetOverrideLogicalHeight(item.FlexedBorderBoxSize());
-  }
-}
-
-namespace {
-
-LayoutUnit MainAxisStaticPositionCommon(const LayoutBox& child,
-                                        LayoutBox* parent,
-                                        LayoutUnit available_space) {
-  LayoutUnit offset = FlexLayoutAlgorithm::InitialContentPositionOffset(
-      parent->StyleRef(), available_space,
-      FlexLayoutAlgorithm::ResolvedJustifyContent(parent->StyleRef()), 1);
-  if (parent->StyleRef().ResolvedIsRowReverseFlexDirection() ||
-      parent->StyleRef().ResolvedIsColumnReverseFlexDirection())
-    offset = available_space - offset;
-  return offset;
-}
-
-LayoutUnit StaticMainAxisPositionForNGPositionedChild(const LayoutBox& child,
-                                                      LayoutBox* parent) {
-  const LayoutUnit available_space =
-      FlexLayoutAlgorithm::IsHorizontalFlow(parent->StyleRef())
-          ? parent->ContentWidth() - child.Size().Width()
-          : parent->ContentHeight() - child.Size().Height();
-  return MainAxisStaticPositionCommon(child, parent, available_space);
-}
-
-LayoutUnit CrossAxisStaticPositionCommon(const LayoutBox& child,
-                                         LayoutBox* parent,
-                                         LayoutUnit available_space) {
-  return FlexItem::AlignmentOffset(
-      available_space,
-      FlexLayoutAlgorithm::AlignmentForChild(parent->StyleRef(),
-                                             child.StyleRef()),
-      LayoutUnit(), parent->StyleRef().FlexWrap() == EFlexWrap::kWrapReverse,
-      parent->StyleRef().IsDeprecatedWebkitBox());
-}
-
-LayoutUnit StaticCrossAxisPositionForNGPositionedChild(const LayoutBox& child,
-                                                       LayoutBox* parent) {
-  const LayoutUnit available_space =
-      FlexLayoutAlgorithm::IsHorizontalFlow(parent->StyleRef())
-          ? parent->ContentHeight() - child.Size().Height()
-          : parent->ContentWidth() - child.Size().Width();
-  return CrossAxisStaticPositionCommon(child, parent, available_space);
-}
-
-LayoutUnit StaticInlinePositionForNGPositionedChild(const LayoutBox& child,
-                                                    LayoutBlock* parent) {
-  const LayoutUnit start_offset = parent->StartOffsetForContent();
-  if (parent->StyleRef().IsDeprecatedWebkitBox())
-    return start_offset;
-  return start_offset +
-         (parent->StyleRef().ResolvedIsColumnFlexDirection()
-              ? StaticCrossAxisPositionForNGPositionedChild(child, parent)
-              : StaticMainAxisPositionForNGPositionedChild(child, parent));
-}
-
-LayoutUnit StaticBlockPositionForNGPositionedChild(const LayoutBox& child,
-                                                   LayoutBlock* parent) {
-  return parent->BorderAndPaddingBefore() +
-         (parent->StyleRef().ResolvedIsColumnFlexDirection()
-              ? StaticMainAxisPositionForNGPositionedChild(child, parent)
-              : StaticCrossAxisPositionForNGPositionedChild(child, parent));
-}
-
-}  // namespace
-
-bool LayoutFlexibleBox::SetStaticPositionForChildInFlexNGContainer(
-    LayoutBox& child,
-    LayoutBlock* parent) {
-  const ComputedStyle& style = parent->StyleRef();
-  bool position_changed = false;
-  PaintLayer* child_layer = child.Layer();
-  if (child.StyleRef().HasStaticInlinePosition(
-          style.IsHorizontalWritingMode())) {
-    LayoutUnit inline_position =
-        StaticInlinePositionForNGPositionedChild(child, parent);
-    if (child_layer->StaticInlinePosition() != inline_position) {
-      child_layer->SetStaticInlinePosition(inline_position);
-      position_changed = true;
-    }
-  }
-  if (child.StyleRef().HasStaticBlockPosition(
-          style.IsHorizontalWritingMode())) {
-    LayoutUnit block_position =
-        StaticBlockPositionForNGPositionedChild(child, parent);
-    if (child_layer->StaticBlockPosition() != block_position) {
-      child_layer->SetStaticBlockPosition(block_position);
-      position_changed = true;
-    }
-  }
-  return position_changed;
-}
-
-LayoutUnit LayoutFlexibleBox::StaticMainAxisPositionForPositionedChild(
-    const LayoutBox& child) {
-  NOT_DESTROYED();
-  const LayoutUnit available_space =
-      MainAxisContentExtent(ContentLogicalHeight()) -
-      MainAxisExtentForChild(child);
-
-  return MainAxisStaticPositionCommon(child, this, available_space);
-}
-
-LayoutUnit LayoutFlexibleBox::StaticCrossAxisPositionForPositionedChild(
-    const LayoutBox& child) {
-  NOT_DESTROYED();
-  LayoutUnit available_space =
-      CrossAxisContentExtent() - CrossAxisExtentForChild(child);
-  return CrossAxisStaticPositionCommon(child, this, available_space);
-}
-
-LayoutUnit LayoutFlexibleBox::StaticInlinePositionForPositionedChild(
-    const LayoutBox& child) {
-  NOT_DESTROYED();
-  const LayoutUnit start_offset = StartOffsetForContent();
-  if (StyleRef().IsDeprecatedWebkitBox())
-    return start_offset;
-  return start_offset + (IsColumnFlow()
-                             ? StaticCrossAxisPositionForPositionedChild(child)
-                             : StaticMainAxisPositionForPositionedChild(child));
-}
-
-LayoutUnit LayoutFlexibleBox::StaticBlockPositionForPositionedChild(
-    const LayoutBox& child) {
-  NOT_DESTROYED();
-  return BorderAndPaddingBefore() +
-         (IsColumnFlow() ? StaticMainAxisPositionForPositionedChild(child)
-                         : StaticCrossAxisPositionForPositionedChild(child));
-}
-
-bool LayoutFlexibleBox::SetStaticPositionForPositionedLayout(LayoutBox& child) {
-  NOT_DESTROYED();
-  bool position_changed = false;
-  PaintLayer* child_layer = child.Layer();
-  if (child.StyleRef().HasStaticInlinePosition(
-          StyleRef().IsHorizontalWritingMode())) {
-    LayoutUnit inline_position = StaticInlinePositionForPositionedChild(child);
-    if (child_layer->StaticInlinePosition() != inline_position) {
-      child_layer->SetStaticInlinePosition(inline_position);
-      position_changed = true;
-    }
-  }
-  if (child.StyleRef().HasStaticBlockPosition(
-          StyleRef().IsHorizontalWritingMode())) {
-    LayoutUnit block_position = StaticBlockPositionForPositionedChild(child);
-    if (child_layer->StaticBlockPosition() != block_position) {
-      child_layer->SetStaticBlockPosition(block_position);
-      position_changed = true;
-    }
-  }
-  return position_changed;
-}
-
-void LayoutFlexibleBox::PrepareChildForPositionedLayout(LayoutBox& child) {
-  NOT_DESTROYED();
-  DCHECK(child.IsOutOfFlowPositioned());
-  child.ContainingBlock()->InsertPositionedObject(&child);
-  PaintLayer* child_layer = child.Layer();
-  LayoutUnit static_inline_position = FlowAwareContentInsetStart();
-  if (child_layer->StaticInlinePosition() != static_inline_position) {
-    child_layer->SetStaticInlinePosition(static_inline_position);
-    if (child.StyleRef().HasStaticInlinePosition(
-            StyleRef().IsHorizontalWritingMode()))
-      child.SetChildNeedsLayout(kMarkOnlyThis);
-  }
-
-  LayoutUnit static_block_position = FlowAwareContentInsetBefore();
-  if (child_layer->StaticBlockPosition() != static_block_position) {
-    child_layer->SetStaticBlockPosition(static_block_position);
-    if (child.StyleRef().HasStaticBlockPosition(
-            StyleRef().IsHorizontalWritingMode()))
-      child.SetChildNeedsLayout(kMarkOnlyThis);
-  }
-}
-
-void LayoutFlexibleBox::ResetAutoMarginsAndLogicalTopInCrossAxis(
-    LayoutBox& child) {
-  NOT_DESTROYED();
-  if (HasAutoMarginsInCrossAxis(child)) {
-    child.UpdateLogicalHeight();
-    if (IsHorizontalFlow()) {
-      if (child.StyleRef().MarginTop().IsAuto())
-        child.SetMarginTop(LayoutUnit());
-      if (child.StyleRef().MarginBottom().IsAuto())
-        child.SetMarginBottom(LayoutUnit());
-    } else {
-      if (child.StyleRef().MarginLeft().IsAuto())
-        child.SetMarginLeft(LayoutUnit());
-      if (child.StyleRef().MarginRight().IsAuto())
-        child.SetMarginRight(LayoutUnit());
-    }
-  }
-}
-
-bool LayoutFlexibleBox::NeedToStretchChildLogicalHeight(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  // This function is a little bit magical. It relies on the fact that blocks
-  // intrinsically "stretch" themselves in their inline axis, i.e. a <div> has
-  // an implicit width: 100%. So the child will automatically stretch if our
-  // cross axis is the child's inline axis. That's the case if:
-  // - We are horizontal and the child is in vertical writing mode
-  // - We are vertical and the child is in horizontal writing mode
-  // Otherwise, we need to stretch if the cross axis size is auto.
-  if (FlexLayoutAlgorithm::AlignmentForChild(StyleRef(), child.StyleRef()) !=
-      ItemPosition::kStretch)
-    return false;
-
-  if (IsHorizontalFlow() != child.StyleRef().IsHorizontalWritingMode())
-    return false;
-
-  return child.StyleRef().LogicalHeight().IsAuto();
-}
-
-bool LayoutFlexibleBox::ChildHasIntrinsicMainAxisSize(
-    const FlexLayoutAlgorithm& algorithm,
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  bool result = false;
-  if (!MainAxisIsInlineAxis(child) && !child.ShouldApplySizeContainment()) {
-    Length child_flex_basis = FlexBasisForChild(child);
-    const Length& child_min_size = IsHorizontalFlow()
-                                       ? child.StyleRef().MinWidth()
-                                       : child.StyleRef().MinHeight();
-    const Length& child_max_size = IsHorizontalFlow()
-                                       ? child.StyleRef().MaxWidth()
-                                       : child.StyleRef().MaxHeight();
-    if (!MainAxisLengthIsDefinite(child, child_flex_basis) ||
-        child_min_size.IsContentOrIntrinsic() ||
-        child_max_size.IsContentOrIntrinsic()) {
-      result = true;
-    } else if (algorithm.ShouldApplyMinSizeAutoForChild(child)) {
-      result = true;
-    }
-  }
-  return result;
-}
-
-EOverflow LayoutFlexibleBox::CrossAxisOverflowForChild(
-    const LayoutBox& child) const {
-  NOT_DESTROYED();
-  if (IsHorizontalFlow())
-    return child.StyleRef().OverflowY();
-  return child.StyleRef().OverflowX();
-}
-
-DISABLE_CFI_PERF
-void LayoutFlexibleBox::LayoutLineItems(FlexLine* current_line,
-                                        bool relayout_children,
-                                        SubtreeLayoutScope& layout_scope,
-                                        FlexOffset** current_item_offset) {
-  NOT_DESTROYED();
-  for (wtf_size_t i = 0; i < current_line->line_items_.size(); ++i) {
-    FlexItem& flex_item = current_line->line_items_[i];
-    LayoutBox* child = flex_item.box_;
-
-    DCHECK(current_item_offset);
-    flex_item.offset_ = *current_item_offset;
-    (*current_item_offset)++;
-
-    DCHECK(!flex_item.box_->IsOutOfFlowPositioned());
-
-    child->SetShouldCheckForPaintInvalidation();
-
-    SetOverrideMainAxisContentSizeForChild(flex_item);
-    // The flexed content size and the override size include the scrollbar
-    // width, so we need to compare to the size including the scrollbar.
-    if (flex_item.flexed_content_size_ !=
-        MainAxisContentExtentForChildIncludingScrollbar(*child)) {
-      child->SetSelfNeedsLayoutForAvailableSpace(true);
-    } else {
-      // To avoid double applying margin changes in
-      // updateAutoMarginsInCrossAxis, we reset the margins here.
-      ResetAutoMarginsAndLogicalTopInCrossAxis(*child);
-    }
-    // We may have already forced relayout for orthogonal flowing children in
-    // computeInnerFlexBaseSizeForChild.
-    bool force_child_relayout =
-        relayout_children && !relaid_out_children_.Contains(child);
-    // TODO(dgrogan): Broaden the NG part of this check once NG types other
-    // than Mixin derivatives are cached.
-    auto* child_layout_block = DynamicTo<LayoutBlock>(child);
-    if (child_layout_block &&
-        child_layout_block->HasPercentHeightDescendants() &&
-        !CanAvoidLayoutForNGChild(*child)) {
-      // Have to force another relayout even though the child is sized
-      // correctly, because its descendants are not sized correctly yet. Our
-      // previous layout of the child was done without an override height set.
-      // So, redo it here.
-      force_child_relayout = true;
-    }
-    UpdateBlockChildDirtyBitsBeforeLayout(force_child_relayout, *child);
-    if (!child->NeedsLayout())
-      MarkChildForPaginationRelayoutIfNeeded(*child, layout_scope);
-    if (child->NeedsLayout()) {
-      relaid_out_children_.insert(child);
-      // It is very important that we only clear the cross axis override size
-      // if we are in fact going to lay out the child. Otherwise, the cross
-      // axis size and the actual laid out size get out of sync, which will
-      // cause problems if we later lay out the child in simplified layout,
-      // which does not go through regular flex layout and therefore would
-      // not reset the cross axis size.
-      if (MainAxisIsInlineAxis(*child))
-        child->ClearOverrideLogicalHeight();
-      else
-        child->ClearOverrideLogicalWidth();
-    }
-    child->LayoutIfNeeded();
-
-    // This shouldn't be necessary, because we set the override size to be
-    // the flexed_content_size and so the result should in fact be that size.
-    // But it turns out that tables ignore the override size, and so we have
-    // to re-check the size so that we place the flex item correctly.
-    flex_item.flexed_content_size_ =
-        MainAxisExtentForChild(*child) - flex_item.main_axis_border_padding_;
-    flex_item.cross_axis_size_ = CrossAxisUnstretchedExtentForChild(*child);
-  }
-}
-
-void LayoutFlexibleBox::ApplyLineItemsPosition(FlexLine* current_line) {
-  NOT_DESTROYED();
-  bool is_paginated = View()->GetLayoutState()->IsPaginated();
-  for (wtf_size_t i = 0; i < current_line->line_items_.size(); ++i) {
-    const FlexItem& flex_item = current_line->line_items_[i];
-    LayoutBox* child = flex_item.box_;
-    SetFlowAwareLocationForChild(*child, *flex_item.offset_);
-    child->SetMargin(flex_item.physical_margins_);
-
-    if (is_paginated)
-      UpdateFragmentationInfoForChild(*child);
-  }
-
-  if (IsColumnFlow()) {
-    SetLogicalHeight(std::max(LogicalHeight(), current_line->main_axis_extent_ +
-                                                   FlowAwareContentInsetEnd()));
-  } else {
-    SetLogicalHeight(
-        std::max(LogicalHeight(), current_line->cross_axis_offset_ +
-                                      FlowAwareContentInsetAfter() +
-                                      current_line->cross_axis_extent_));
-  }
-
-  if (StyleRef().ResolvedIsColumnReverseFlexDirection()) {
-    // We have to do an extra pass for column-reverse to reposition the flex
-    // items since the start depends on the height of the flexbox, which we
-    // only know after we've positioned all the flex items.
-    UpdateLogicalHeight();
-    LayoutColumnReverse(current_line->line_items_,
-                        current_line->cross_axis_offset_,
-                        current_line->remaining_free_space_);
-  }
-}
-
-void LayoutFlexibleBox::LayoutColumnReverse(FlexItemVectorView& children,
-                                            LayoutUnit cross_axis_offset,
-                                            LayoutUnit available_free_space) {
-  NOT_DESTROYED();
-  const StyleContentAlignmentData justify_content =
-      FlexLayoutAlgorithm::ResolvedJustifyContent(StyleRef());
-
-  // This is similar to the logic in FlexLine::ComputeLineItemsPosition, except
-  // we place the children starting from the end of the flexbox.
-  LayoutUnit main_axis_offset = LogicalHeight() - FlowAwareContentInsetEnd();
-  main_axis_offset -= FlexLayoutAlgorithm::InitialContentPositionOffset(
-      StyleRef(), available_free_space, justify_content, children.size());
-
-  for (wtf_size_t i = 0; i < children.size(); ++i) {
-    FlexItem& flex_item = children[i];
-    LayoutBox* child = flex_item.box_;
-
-    DCHECK(!child->IsOutOfFlowPositioned());
-
-    main_axis_offset -=
-        MainAxisExtentForChild(*child) + flex_item.FlowAwareMarginEnd();
-
-    SetFlowAwareLocationForChild(
-        *child,
-        FlexOffset(main_axis_offset,
-                   cross_axis_offset + flex_item.FlowAwareMarginBefore()));
-
-    main_axis_offset -= flex_item.FlowAwareMarginStart();
-
-    main_axis_offset -=
-        FlexLayoutAlgorithm::ContentDistributionSpaceBetweenChildren(
-            available_free_space, justify_content, children.size());
-  }
-}
-
-void LayoutFlexibleBox::AlignFlexLines(FlexLayoutAlgorithm& algorithm) {
-  NOT_DESTROYED();
-  Vector<FlexLine>& line_contexts = algorithm.FlexLines();
-  const StyleContentAlignmentData align_content =
-      FlexLayoutAlgorithm::ResolvedAlignContent(StyleRef());
-  if (align_content.GetPosition() == ContentPosition::kFlexStart &&
-      algorithm.gap_between_lines_ == 0) {
-    return;
-  }
-
-  if (IsMultiline() && !line_contexts.empty()) {
-    UseCounter::Count(GetDocument(),
-                      WebFeature::kFlexboxSingleLineAlignContent);
-  }
-
-  algorithm.AlignFlexLines(CrossAxisContentExtent());
-  for (unsigned line_number = 0; line_number < line_contexts.size();
-       ++line_number) {
-    FlexLine& line_context = line_contexts[line_number];
-    for (FlexItem& flex_item : line_context.line_items_) {
-      ResetAlignmentForChild(*flex_item.box_,
-                             flex_item.offset_->cross_axis_offset);
-    }
-  }
-}
-
-void LayoutFlexibleBox::ResetAlignmentForChild(
-    LayoutBox& child,
-    LayoutUnit new_cross_axis_position) {
-  NOT_DESTROYED();
-  SetFlowAwareLocationForChild(
-      child, {FlowAwareLocationForChild(child).main_axis_offset,
-              new_cross_axis_position});
-}
-
-void LayoutFlexibleBox::AlignChildren(FlexLayoutAlgorithm& algorithm) {
-  NOT_DESTROYED();
-  Vector<FlexLine>& line_contexts = algorithm.FlexLines();
-
-  algorithm.AlignChildren();
-  for (unsigned line_number = 0; line_number < line_contexts.size();
-       ++line_number) {
-    FlexLine& line_context = line_contexts[line_number];
-    for (FlexItem& flex_item : line_context.line_items_) {
-      if (flex_item.needs_relayout_for_stretch_) {
-        DCHECK(flex_item.Alignment() == ItemPosition::kStretch);
-        ApplyStretchAlignmentToChild(flex_item);
-        flex_item.needs_relayout_for_stretch_ = false;
-      }
-      ResetAlignmentForChild(*flex_item.box_,
-                             flex_item.offset_->cross_axis_offset);
-      flex_item.box_->SetMargin(flex_item.physical_margins_);
-    }
-  }
-}
-
-void LayoutFlexibleBox::ApplyStretchAlignmentToChild(FlexItem& flex_item) {
-  NOT_DESTROYED();
-  LayoutBox& child = *flex_item.box_;
-  if (flex_item.MainAxisIsInlineAxis() &&
-      child.StyleRef().LogicalHeight().IsAuto()) {
-    // FIXME: Can avoid laying out here in some cases. See
-    // https://webkit.org/b/87905.
-    bool child_needs_relayout =
-        flex_item.cross_axis_size_ != child.LogicalHeight();
-    child.SetOverrideLogicalHeight(flex_item.cross_axis_size_);
-
-    auto* child_block = DynamicTo<LayoutBlock>(child);
-    if (child_block && child_block->HasPercentHeightDescendants() &&
-        !CanAvoidLayoutForNGChild(child)) {
-      // Have to force another relayout even though the child is sized
-      // correctly, because its descendants are not sized correctly yet. Our
-      // previous layout of the child was done without an override height set.
-      // So, redo it here.
-      child_needs_relayout |= relaid_out_children_.Contains(&child);
-    }
-    if (child_needs_relayout)
-      child.ForceLayout();
-  } else if (!flex_item.MainAxisIsInlineAxis() &&
-             child.StyleRef().LogicalWidth().IsAuto()) {
-    if (flex_item.cross_axis_size_ != child.LogicalWidth()) {
-      child.SetOverrideLogicalWidth(flex_item.cross_axis_size_);
-      child.ForceLayout();
-    }
-  }
-}
-
-void LayoutFlexibleBox::FlipForRightToLeftColumn(
-    const Vector<FlexLine>& line_contexts) {
-  NOT_DESTROYED();
-  if (StyleRef().IsLeftToRightDirection() || !IsColumnFlow())
-    return;
-
-  LayoutUnit cross_extent = CrossAxisExtent();
-  for (const FlexLine& line_context : line_contexts) {
-    for (const FlexItem& flex_item : line_context.line_items_) {
-      DCHECK(!flex_item.box_->IsOutOfFlowPositioned());
-
-      FlexOffset offset = FlowAwareLocationForChild(*flex_item.box_);
-      // For vertical flows, setFlowAwareLocationForChild will transpose x and
-      // y, so using the y axis for a column cross axis extent is correct.
-      offset.cross_axis_offset =
-          cross_extent - flex_item.cross_axis_size_ - offset.cross_axis_offset;
-      SetFlowAwareLocationForChild(*flex_item.box_, offset);
-    }
-  }
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.h b/third_party/blink/renderer/core/layout/layout_flexible_box.h
deleted file mode 100644
index aab2fde..0000000
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.h
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_FLEXIBLE_BOX_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_FLEXIBLE_BOX_H_
-
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/geometry/flex_offset.h"
-#include "third_party/blink/renderer/core/layout/layout_block.h"
-#include "third_party/blink/renderer/core/layout/order_iterator.h"
-#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
-#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
-
-namespace blink {
-
-class FlexItem;
-class FlexItemVectorView;
-class FlexLayoutAlgorithm;
-class FlexLine;
-struct MinMaxSizes;
-
-class CORE_EXPORT LayoutFlexibleBox : public LayoutBlock {
- public:
-  explicit LayoutFlexibleBox(Element*);
-  ~LayoutFlexibleBox() override;
-  void Trace(Visitor*) const override;
-
-  const char* GetName() const override {
-    NOT_DESTROYED();
-    return "LayoutFlexibleBox";
-  }
-
-  bool IsFlexibleBox() const final {
-    NOT_DESTROYED();
-    return true;
-  }
-  bool IsFlexibleBoxIncludingNG() const final {
-    NOT_DESTROYED();
-    return true;
-  }
-  void UpdateBlockLayout(bool relayout_children) final;
-
-  bool IsChildAllowed(LayoutObject* object,
-                      const ComputedStyle& style) const override;
-  LayoutUnit BaselinePosition(
-      FontBaseline,
-      bool first_line,
-      LineDirectionMode,
-      LinePositionMode = kPositionOnContainingLine) const override;
-
-  static LayoutUnit SynthesizedBaselineFromBorderBox(const LayoutBox&,
-                                                     LineDirectionMode);
-
-  LayoutUnit FirstLineBoxBaseline() const override;
-  LayoutUnit InlineBlockBaseline(LineDirectionMode) const override;
-  bool HasTopOverflow() const override;
-  bool HasLeftOverflow() const override;
-
-  void PaintChildren(const PaintInfo&,
-                     const PhysicalOffset& paint_offset) const final;
-
-  bool IsHorizontalFlow() const;
-
-  const OrderIterator& GetOrderIterator() const {
-    NOT_DESTROYED();
-    return order_iterator_;
-  }
-
-  // These three functions are used when resolving percentages against a
-  // flex item's logical height. In flexbox, sometimes a logical height
-  // should be considered definite even though it normally shouldn't be,
-  // and these functions implement that logic.
-  bool CrossSizeIsDefiniteForPercentageResolution(const LayoutBox& child) const;
-  bool MainSizeIsDefiniteForPercentageResolution(const LayoutBox& child) const;
-  bool UseOverrideLogicalHeightForPerentageResolution(
-      const LayoutBox& child) const;
-
-  void ClearCachedMainSizeForChild(const LayoutBox& child);
-
-  LayoutUnit StaticMainAxisPositionForPositionedChild(const LayoutBox& child);
-  LayoutUnit StaticCrossAxisPositionForPositionedChild(const LayoutBox& child);
-
-  LayoutUnit StaticInlinePositionForPositionedChild(const LayoutBox& child);
-  LayoutUnit StaticBlockPositionForPositionedChild(const LayoutBox& child);
-
-  // Returns true if the position changed. In that case, the child will have to
-  // be laid out again.
-  bool SetStaticPositionForPositionedLayout(LayoutBox& child);
-  static bool SetStaticPositionForChildInFlexNGContainer(LayoutBox& child,
-                                                         LayoutBlock* parent);
-  LayoutUnit CrossAxisContentExtent() const;
-
- protected:
-  MinMaxSizes ComputeIntrinsicLogicalWidths() const override;
-
-  bool HitTestChildren(HitTestResult&,
-                       const HitTestLocation&,
-                       const PhysicalOffset& accumulated_offset,
-                       HitTestPhase) override;
-
-  void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
-  void RemoveChild(LayoutObject*) override;
-
- private:
-  enum ChildLayoutType { kLayoutIfNeeded, kForceLayout, kNeverLayout };
-
-  enum class SizeDefiniteness { kDefinite, kIndefinite, kUnknown };
-
-  bool MainAxisIsInlineAxis(const LayoutBox& child) const;
-  bool IsColumnFlow() const;
-  bool IsLeftToRightFlow() const;
-  bool IsMultiline() const;
-  Length FlexBasisForChild(const LayoutBox& child) const;
-  LayoutUnit CrossAxisExtentForChild(const LayoutBox& child) const;
-  LayoutUnit CrossAxisUnstretchedExtentForChild(const LayoutBox& child) const;
-  LayoutUnit ChildUnstretchedLogicalHeight(const LayoutBox& child) const;
-  LayoutUnit ChildUnstretchedLogicalWidth(const LayoutBox& child) const;
-  LayoutUnit MainAxisExtentForChild(const LayoutBox& child) const;
-  LayoutUnit MainAxisContentExtentForChild(const LayoutBox& child) const;
-  LayoutUnit MainAxisContentExtentForChildIncludingScrollbar(
-      const LayoutBox& child) const;
-  LayoutUnit CrossAxisExtent() const;
-  LayoutUnit MainAxisContentExtent(LayoutUnit content_logical_height);
-  LayoutUnit ComputeMainAxisExtentForChild(const LayoutBox& child,
-                                           SizeType,
-                                           const Length& size,
-                                           LayoutUnit border_and_padding) const;
-
-  LayoutUnit ContentInsetBottom() const;
-  LayoutUnit ContentInsetRight() const;
-  LayoutUnit FlowAwareContentInsetStart() const;
-  LayoutUnit FlowAwareContentInsetEnd() const;
-  LayoutUnit FlowAwareContentInsetBefore() const;
-  LayoutUnit FlowAwareContentInsetAfter() const;
-
-  LayoutUnit CrossAxisScrollbarExtent() const;
-  LayoutUnit CrossAxisScrollbarExtentForChild(const LayoutBox& child) const;
-  FlexOffset FlowAwareLocationForChild(const LayoutBox& child) const;
-  bool UseChildAspectRatio(const LayoutBox& child) const;
-  LayoutUnit ComputeMainSizeFromAspectRatioUsing(
-      const LayoutBox& child,
-      const Length& cross_size_length,
-      LayoutUnit main_axis_border_and_padding,
-      LayoutUnit cross_axis_border_and_padding) const;
-  void SetFlowAwareLocationForChild(LayoutBox& child, const FlexOffset&);
-  LayoutUnit ComputeInnerFlexBaseSizeForChild(
-      LayoutBox& child,
-      LayoutUnit main_axis_border_and_padding,
-      LayoutUnit cross_axis_border_and_padding,
-      ChildLayoutType = kLayoutIfNeeded);
-  void ResetAlignmentForChild(LayoutBox& child, LayoutUnit);
-  bool MainAxisLengthIsDefinite(const LayoutBox& child,
-                                const Length& flex_basis,
-                                bool add_to_cb = true) const;
-  bool CrossAxisLengthIsDefinite(const LayoutBox& child,
-                                 const Length& flex_basis) const;
-  bool NeedToStretchChildLogicalHeight(const LayoutBox& child) const;
-  bool ChildHasIntrinsicMainAxisSize(const FlexLayoutAlgorithm&,
-                                     const LayoutBox& child) const;
-  EOverflow MainAxisOverflowForChild(const LayoutBox& child) const;
-  EOverflow CrossAxisOverflowForChild(const LayoutBox& child) const;
-  void CacheChildMainSize(const LayoutBox& child);
-  bool CanAvoidLayoutForNGChild(const LayoutBox& child) const;
-
-  void LayoutFlexItems(bool relayout_children, SubtreeLayoutScope&);
-  bool HasAutoMarginsInCrossAxis(const LayoutBox& child) const;
-  void RepositionLogicalHeightDependentFlexItems(
-      FlexLayoutAlgorithm& algorithm);
-  LayoutUnit ClientLogicalBottomAfterRepositioning();
-
-  LayoutUnit ComputeChildMarginValue(const Length& margin);
-  void PrepareOrderIteratorAndMargins();
-  MinMaxSizes ComputeMinAndMaxSizesForChild(
-      const FlexLayoutAlgorithm& algorithm,
-      const LayoutBox& child,
-      LayoutUnit border_and_padding,
-      LayoutUnit cross_axis_border_and_padding) const;
-  LayoutUnit AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
-      const LayoutBox& child,
-      LayoutUnit child_size,
-      LayoutUnit main_axis_border_and_padding,
-      LayoutUnit cross_axis_border_and_padding) const;
-  void ConstructAndAppendFlexItem(FlexLayoutAlgorithm* algorithm,
-                                  LayoutBox& child,
-                                  ChildLayoutType);
-
-  bool ResolveFlexibleLengths(FlexLine*,
-                              LayoutUnit initial_free_space,
-                              LayoutUnit& remaining_free_space);
-
-  void ResetAutoMarginsAndLogicalTopInCrossAxis(LayoutBox& child);
-  void SetOverrideMainAxisContentSizeForChild(FlexItem&);
-  void PrepareChildForPositionedLayout(LayoutBox& child);
-  void LayoutLineItems(FlexLine*,
-                       bool relayout_children,
-                       SubtreeLayoutScope&,
-                       FlexOffset** current_item_offset);
-  void ApplyLineItemsPosition(FlexLine*);
-  void LayoutColumnReverse(FlexItemVectorView&,
-                           LayoutUnit cross_axis_offset,
-                           LayoutUnit available_free_space);
-  void AlignFlexLines(FlexLayoutAlgorithm&);
-  void AlignChildren(FlexLayoutAlgorithm&);
-  void ApplyStretchAlignmentToChild(FlexItem& child);
-  void FlipForRightToLeftColumn(const Vector<FlexLine>& line_contexts);
-
-  float CountIntrinsicSizeForAlgorithmChange(
-      LayoutUnit max_preferred_width,
-      LayoutBox* child,
-      float previous_max_content_flex_fraction) const;
-
-  void MergeAnonymousFlexItems(LayoutObject* remove_child);
-
-  // This is used to cache the preferred size for orthogonal flow children so we
-  // don't have to relayout to get it
-  HeapHashMap<Member<const LayoutObject>, LayoutUnit>
-      intrinsic_size_along_main_axis_;
-
-  // This set is used to keep track of which children we laid out in this
-  // current layout iteration. We need it because the ones in this set may
-  // need an additional layout pass for correct stretch alignment handling, as
-  // the first layout likely did not use the correct value for percentage
-  // sizing of children.
-  HeapHashSet<Member<const LayoutObject>> relaid_out_children_;
-
-  mutable OrderIterator order_iterator_;
-  int number_of_in_flow_children_on_first_line_;
-
-  // This is SizeIsUnknown outside of layoutBlock()
-  mutable SizeDefiniteness has_definite_height_;
-  bool in_layout_;
-};
-
-template <>
-struct DowncastTraits<LayoutFlexibleBox> {
-  static bool AllowFrom(const LayoutObject& object) {
-    return object.IsFlexibleBox();
-  }
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_FLEXIBLE_BOX_H_
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc b/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc
index f3877a8..46cdfcf 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box_test.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 "third_party/blink/renderer/core/layout/layout_flexible_box.h"
-
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index ded7e8e9..5311454 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -76,7 +76,6 @@
 #include "third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_fieldset.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/layout_flow_thread.h"
 #include "third_party/blink/renderer/core/layout/layout_image.h"
 #include "third_party/blink/renderer/core/layout/layout_image_resource_style_image.h"
@@ -92,6 +91,7 @@
 #include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_combine.h"
 #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h"
 #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h"
@@ -393,11 +393,11 @@
       }
       UseCounter::Count(element->GetDocument(),
                         WebFeature::kWebkitBoxWithoutWebkitLineClamp);
-      return LayoutObjectFactory::CreateFlexibleBox(*element, style, legacy);
+      return MakeGarbageCollected<LayoutNGFlexibleBox>(element);
     case EDisplay::kFlex:
     case EDisplay::kInlineFlex:
       UseCounter::Count(element->GetDocument(), WebFeature::kCSSFlexibleBox);
-      return LayoutObjectFactory::CreateFlexibleBox(*element, style, legacy);
+      return MakeGarbageCollected<LayoutNGFlexibleBox>(element);
     case EDisplay::kGrid:
     case EDisplay::kInlineGrid:
       UseCounter::Count(element->GetDocument(), WebFeature::kCSSGridLayout);
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index cb171b0da..6173eb1 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -2963,13 +2963,7 @@
 
   void Destroy();
 
-  // Virtual function helper for the new FlexibleBox Layout (display:
-  // -webkit-flex).
-  virtual bool IsFlexibleBox() const {
-    NOT_DESTROYED();
-    return false;
-  }
-
+  // TODO(1229581): Rename this function.
   virtual bool IsFlexibleBoxIncludingNG() const {
     NOT_DESTROYED();
     return false;
diff --git a/third_party/blink/renderer/core/layout/layout_object_factory.cc b/third_party/blink/renderer/core/layout/layout_object_factory.cc
index 1a2f25a7..439d10fc 100644
--- a/third_party/blink/renderer/core/layout/layout_object_factory.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_factory.cc
@@ -12,7 +12,6 @@
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
 #include "third_party/blink/renderer/core/layout/layout_counter.h"
 #include "third_party/blink/renderer/core/layout/layout_fieldset.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/layout_frame_set.h"
 #include "third_party/blink/renderer/core/layout/layout_inside_list_marker.h"
 #include "third_party/blink/renderer/core/layout/layout_list_item.h"
@@ -132,13 +131,6 @@
   return MakeGarbageCollected<LayoutNGView>(&document);
 }
 
-LayoutBlock* LayoutObjectFactory::CreateFlexibleBox(Node& node,
-                                                    const ComputedStyle& style,
-                                                    LegacyLayout legacy) {
-  return CreateObject<LayoutBlock, LayoutNGFlexibleBox, LayoutFlexibleBox>(
-      node, legacy);
-}
-
 LayoutBlock* LayoutObjectFactory::CreateMath(Node& node,
                                              const ComputedStyle& style,
                                              LegacyLayout legacy) {
diff --git a/third_party/blink/renderer/core/layout/layout_object_factory.h b/third_party/blink/renderer/core/layout/layout_object_factory.h
index 7a1b2cd6..ab40696 100644
--- a/third_party/blink/renderer/core/layout/layout_object_factory.h
+++ b/third_party/blink/renderer/core/layout/layout_object_factory.h
@@ -46,9 +46,6 @@
                                           const ComputedStyle&,
                                           LegacyLayout);
   static LayoutView* CreateView(Document&, const ComputedStyle&);
-  static LayoutBlock* CreateFlexibleBox(Node&,
-                                        const ComputedStyle&,
-                                        LegacyLayout);
   static LayoutBlock* CreateMath(Node&, const ComputedStyle&, LegacyLayout);
   static LayoutBlock* CreateCustom(Node&, const ComputedStyle&, LegacyLayout);
   static LayoutObject* CreateListMarker(Node&,
diff --git a/third_party/blink/renderer/core/layout/layout_text_control.h b/third_party/blink/renderer/core/layout/layout_text_control.h
index 8ee25bc..e3e3734 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control.h
+++ b/third_party/blink/renderer/core/layout/layout_text_control.h
@@ -25,7 +25,6 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
index 40f833a..455c54e 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -10,7 +10,6 @@
 #include "third_party/blink/renderer/core/layout/flexible_box_algorithm.h"
 #include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.h"
 #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_data.h"
@@ -993,7 +992,7 @@
         /* is_parallel_context */ !is_column_, is_last_baseline,
         /* is_flipped */ is_wrap_reverse);
     algorithm_
-        .emplace_back(nullptr, child.Style(), flex_base_content_size,
+        .emplace_back(child.Style(), flex_base_content_size,
                       min_max_sizes_in_main_axis_direction,
                       min_max_sizes_in_cross_axis_direction,
                       main_axis_border_padding, cross_axis_border_padding,
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index 2eacc798..a1c3d6e5 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -1433,8 +1433,7 @@
     return 0;
   }
 
-  const wtf_size_t tracks_per_repeat =
-      track_list.RepeaterCount() * track_list.AutoRepeatTrackCount();
+  const wtf_size_t tracks_per_repeat = track_list.AutoRepeatTrackCount();
   if (tracks_per_repeat > subgrid_span_size) {
     // No room left for auto repetitions because each repetition is too large.
     return 0;
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.cc
index 91aa9f1..366714ea 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.cc
@@ -122,7 +122,7 @@
     // merging. These are positive and relative to index 0, so we only need to
     // clamp values above `subgrid_span_size`.
     for (const auto& pair : subgrid_map) {
-      WTF::Vector<wtf_size_t> clamped_list;
+      Vector<wtf_size_t> clamped_list;
       for (const auto& position : pair.value) {
         if (position > subgrid_span_size)
           clamped_list.push_back(subgrid_span_size);
@@ -136,7 +136,7 @@
     // (`parent_map`). The map is a key-value store with keys as the implicit
     // line name and the value as an array of ascending indices.
     for (const auto& pair : parent_map) {
-      WTF::Vector<wtf_size_t> merged_list;
+      Vector<wtf_size_t> merged_list;
       for (const auto& position : pair.value) {
         auto IsGridAreaInSubgridRange = [&]() -> bool {
           // Returns true if a given position is within either the implicit
@@ -224,8 +224,8 @@
       [](NamedGridLinesMap& subgrid_map,
          const NamedGridLinesMap& parent_auto_repeat_map,
          const blink::ComputedGridTrackList& track_list, GridSpan subgrid_span,
-         wtf_size_t auto_repetitions,
-         bool is_opposite_direction_to_parent) -> void {
+         wtf_size_t auto_repetitions, bool is_opposite_direction_to_parent,
+         bool is_nested_subgrid) -> void {
     const wtf_size_t auto_repeat_track_count =
         track_list.TrackList().AutoRepeatTrackCount();
     const wtf_size_t auto_repeat_total_tracks =
@@ -238,24 +238,27 @@
     // come after the auto repeater. This is because they were parsed without
     // knowledge of the number of repeats. Now that we know how many auto
     // repeats there are, we need to shift the existing entries by the total
-    // number of auto repeat tracks.
+    // number of auto repeat tracks. For now, skip this on nested subgrids,
+    // as it will double-shift non-auto repeat lines.
+    // TODO(kschmi): Properly shift line names after the insertion point for
+    // nested subgrids. This should happen in `MergeNamedGridLinesWithParent`.
     // TODO(kschmi): Do we also need to do this for implicit lines?
     const wtf_size_t insertion_point = track_list.auto_repeat_insertion_point;
-    const wtf_size_t last_auto_repeat_index =
-        insertion_point + auto_repeat_total_tracks;
-    for (const auto& pair : subgrid_map) {
-      WTF::Vector<wtf_size_t> shifted_list;
-      for (const auto& position : pair.value) {
-        if (position >= insertion_point) {
-          wtf_size_t expanded_position = position + last_auto_repeat_index;
-          // These have already been offset relative to index 0, so explicitly
-          // do not offset by `subgrid_span` like we do below.
-          if (subgrid_span.Contains(expanded_position)) {
-            shifted_list.push_back(expanded_position);
+    if (!is_nested_subgrid) {
+      for (const auto& pair : subgrid_map) {
+        Vector<wtf_size_t> shifted_list;
+        for (const auto& position : pair.value) {
+          if (position >= insertion_point) {
+            wtf_size_t expanded_position = position + auto_repeat_total_tracks;
+            // These have already been offset relative to index 0, so explicitly
+            // do not offset by `subgrid_span` like we do below.
+            if (subgrid_span.Contains(expanded_position)) {
+              shifted_list.push_back(expanded_position);
+            }
           }
         }
+        subgrid_map.Set(pair.key, shifted_list);
       }
-      subgrid_map.Set(pair.key, shifted_list);
     }
 
     // Now expand the auto repeaters into `subgrid_map`.
@@ -308,12 +311,15 @@
       }
     }
   };
-
-  // TODO(kschmi) - Account for orthogonal writing modes and swap rows/columns.
   const bool is_opposite_direction_to_parent =
-      (grid_style.Direction() != parent_line_resolver.style_->Direction());
+      grid_style.Direction() != parent_line_resolver.style_->Direction();
+  const bool is_parallel_to_parent =
+      IsParallelWritingMode(grid_style.GetWritingMode(),
+                            parent_line_resolver.style_->GetWritingMode());
 
   if (subgrid_area.columns.IsTranslatedDefinite()) {
+    const auto track_direction_in_parent =
+        is_parallel_to_parent ? kForColumns : kForRows;
     MergeNamedGridLinesWithParent(
         *subgridded_columns_merged_explicit_grid_line_names_,
         parent_line_resolver.ExplicitNamedLinesMap(kForColumns),
@@ -323,29 +329,39 @@
         parent_line_resolver.ImplicitNamedLinesMap(kForColumns),
         subgrid_area.columns);
     // Expand auto repeaters from the parent into the named line map.
+    // TODO(kschmi): Also expand the subgrid's repeaters. Otherwise, we could
+    // have issues with precedence.
     ExpandAutoRepeatTracksFromParent(
         *subgridded_columns_merged_explicit_grid_line_names_,
-        parent_line_resolver.AutoRepeatLineNamesMap(kForColumns),
-        parent_line_resolver.ComputedGridTrackList(kForColumns),
-        subgrid_area.columns, parent_line_resolver.AutoRepetitions(kForColumns),
-        is_opposite_direction_to_parent);
+        parent_line_resolver.AutoRepeatLineNamesMap(track_direction_in_parent),
+        parent_line_resolver.ComputedGridTrackList(track_direction_in_parent),
+        subgrid_area.columns,
+        parent_line_resolver.AutoRepetitions(track_direction_in_parent),
+        is_opposite_direction_to_parent,
+        parent_line_resolver.IsSubgridded(track_direction_in_parent));
   }
   if (subgrid_area.rows.IsTranslatedDefinite()) {
+    const auto track_direction_in_parent =
+        is_parallel_to_parent ? kForRows : kForColumns;
     MergeNamedGridLinesWithParent(
         *subgridded_rows_merged_explicit_grid_line_names_,
-        parent_line_resolver.ExplicitNamedLinesMap(kForRows), subgrid_area.rows,
-        is_opposite_direction_to_parent);
+        parent_line_resolver.ExplicitNamedLinesMap(track_direction_in_parent),
+        subgrid_area.rows, is_opposite_direction_to_parent);
     MergeImplicitLinesWithParent(
         *subgridded_rows_merged_implicit_grid_line_names_,
         parent_line_resolver.ImplicitNamedLinesMap(kForRows),
         subgrid_area.rows);
     // Expand auto repeaters from the parent into the named line map.
+    // TODO(kschmi): Also expand the subgrid's repeaters. Otherwise, we could
+    // have issues with precedence.
     ExpandAutoRepeatTracksFromParent(
         *subgridded_rows_merged_explicit_grid_line_names_,
-        parent_line_resolver.AutoRepeatLineNamesMap(kForRows),
-        parent_line_resolver.ComputedGridTrackList(kForRows), subgrid_area.rows,
-        parent_line_resolver.AutoRepetitions(kForRows),
-        is_opposite_direction_to_parent);
+        parent_line_resolver.AutoRepeatLineNamesMap(track_direction_in_parent),
+        parent_line_resolver.ComputedGridTrackList(track_direction_in_parent),
+        subgrid_area.rows,
+        parent_line_resolver.AutoRepetitions(track_direction_in_parent),
+        is_opposite_direction_to_parent,
+        parent_line_resolver.IsSubgridded(track_direction_in_parent));
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_named_line_collection.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_named_line_collection.cc
index 4f61aaed..c884ac26 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_named_line_collection.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_named_line_collection.cc
@@ -8,7 +8,6 @@
 #include "third_party/blink/renderer/core/style/computed_grid_track_list.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/style/grid_area.h"
-#include "third_party/blink/renderer/core/style/grid_positions_resolver.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
index 0cc2302..416f22557 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
@@ -226,8 +226,13 @@
     DCHECK_GT(range.track_count, 0u);
 
     // Compute current repeater's index, size, and offset.
+    // TODO(ethavar): Simplify this logic.
     range.begin_set_index = current_set_index;
-    if (current_explicit_repeater_index != kNotFound) {
+    if (explicit_tracks_.IsSubgriddedAxis()) {
+      // Subgridded axis specified on standalone grid, use 'auto'.
+      range.repeater_index = kNotFound;
+      range.repeater_offset = 0u;
+    } else if (current_explicit_repeater_index != kNotFound) {
       current_repeater_size =
           explicit_tracks_.RepeatSize(current_explicit_repeater_index);
 
@@ -913,10 +918,12 @@
     };
 
     if (range.repeater_index == kNotFound) {
-      // The only case where a range doesn't have a repeater index is when the
-      // range is in the implicit grid and there are no auto track definitions;
-      // fill the entire range with a single set of 'auto' tracks.
-      DCHECK(range.IsImplicit());
+      // The only cases where a range doesn't have a repeater index are when the
+      // range is in the implicit grid and there are no auto track definitions,
+      // or when 'subgrid' is specified on a track definition but it's not a
+      // child of a grid (and thus not a subgrid); in both cases, fill the
+      // entire range with a single set of 'auto' tracks.
+      DCHECK(range.IsImplicit() || explicit_track_list.IsSubgriddedAxis());
       CacheSetProperties(sets_.emplace_back(range.track_count));
     } else {
       const auto& specified_track_list =
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index 1b557dd4..8815bdd1 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -291,24 +291,6 @@
                                        /* relative_offset */ LogicalOffset()));
 }
 
-void NGBoxFragmentBuilder::RemoveOldLegacyOOFFlexItem(
-    const LayoutObject& object) {
-  // While what this method does should "work" for any child fragment in
-  // general, it's only expected to be called under very specific legacy
-  // circumstances, and besides it's evil.
-  DCHECK(object.IsOutOfFlowPositioned());
-  DCHECK(object.Parent()->IsFlexibleBox());
-  DCHECK(object.Parent()->IsOutOfFlowPositioned());
-  for (wtf_size_t idx = 0; idx < children_.size(); idx++) {
-    const NGLogicalLink& child = children_[idx];
-    if (child.fragment->GetLayoutObject() == &object) {
-      children_.EraseAt(idx);
-      return;
-    }
-  }
-  NOTREACHED();
-}
-
 NGPhysicalFragment::NGBoxType NGBoxFragmentBuilder::BoxType() const {
   if (box_type_ != NGPhysicalFragment::NGBoxType::kNormalBox)
     return box_type_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index 0de8b3a2..a198113e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -236,14 +236,6 @@
                                    const NGLogicalStaticPosition&,
                                    const LayoutInline* inline_container);
 
-  // Remove the fragment previously generated for an out-of-flow positioned flex
-  // item inside an out-of-flow legacy flex container. This is a work-around for
-  // OOFs being laid out out-of-document-order, which is an issue with the
-  // legacy engine (although it's not known to cause any other actual problems
-  // than this). We'll call this method to correct a document-out-of-order
-  // issue.
-  void RemoveOldLegacyOOFFlexItem(const LayoutObject&);
-
   // Before layout we'll determine whether we can tell for sure that the node
   // (or what's left of it to lay out, in case we've already broken) will fit in
   // the current fragmentainer. If this is the case, we'll know that any
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
index 4472e206..27eb89e20 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "third_party/blink/renderer/core/layout/layout_block.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/ng/layout_box_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
@@ -74,14 +73,6 @@
     available_size.block_size = block.OverrideLogicalHeight();
     is_fixed_block_size = true;
   }
-  if (block.IsFlexItem() && is_fixed_block_size) {
-    // The flexbox-specific behavior is in addition to regular definite-ness, so
-    // if the flex item would normally have a definite height it should keep it.
-    is_initial_block_size_definite =
-        To<LayoutFlexibleBox>(block.Parent())
-            ->UseOverrideLogicalHeightForPerentageResolution(block) ||
-        block.HasDefiniteLogicalHeight();
-  }
 
   // We cannot enter NG layout at an object that isn't a formatting context
   // root. However, even though we're creating a constraint space for an object
@@ -105,7 +96,7 @@
                                          : NGBaselineAlgorithmType::kDefault);
   }
 
-  if (block.IsAtomicInlineLevel() || block.IsFlexItem() || block.IsFloating()) {
+  if (block.IsAtomicInlineLevel() || block.IsFloating()) {
     builder.SetIsPaintedAtomically(true);
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index 693a2e1b..a298562 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -10,7 +10,6 @@
 #include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h"
 #include "third_party/blink/renderer/core/layout/layout_block.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h"
@@ -298,37 +297,9 @@
     if (placed_objects.Contains(legacy_object)) {
       if (!performing_extra_legacy_check_ || !legacy_object->NeedsLayout())
         continue;
-      container_builder_->RemoveOldLegacyOOFFlexItem(*legacy_object);
     }
 
-    // Flex OOF children may have center alignment or similar, and in order
-    // to determine their static position correctly need to have a valid
-    // size first.
-    // We perform a pre-layout to correctly determine the static position.
-    // Copied from LayoutBlock::LayoutPositionedObject
-    // TODO(layout-dev): Remove this once LayoutFlexibleBox is removed.
     LayoutBox* layout_box = To<LayoutBox>(legacy_object);
-    if (layout_box->Parent()->IsFlexibleBox()) {
-      auto* parent = To<LayoutFlexibleBox>(layout_box->Parent());
-      if (parent->SetStaticPositionForPositionedLayout(*layout_box)) {
-        NGLogicalOutOfFlowPositionedNode candidate((NGBlockNode(layout_box)),
-                                                   NGLogicalStaticPosition());
-        NodeInfo node_info = SetupNodeInfo(candidate);
-        NodeToLayout node_to_layout = {
-            node_info, CalculateOffset(node_info, /* only_layout */ nullptr)};
-        LayoutOOFNode(node_to_layout,
-                      /* only_layout */ nullptr);
-        parent->SetStaticPositionForPositionedLayout(*layout_box);
-      }
-    }
-
-    // If we have a legacy OOF flex container, we'll allow some rocket science
-    // to take place, as an attempt to get things laid out in correct document
-    // order, or we might otherwise leave behind objects (OOF flex items)
-    // needing layout.
-    if (!has_legacy_flex_box_)
-      has_legacy_flex_box_ = layout_box->IsFlexibleBox();
-
     NGLogicalStaticPosition static_position =
         LayoutBoxUtils::ComputeStaticPositionFromLegacy(
             *layout_box,
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index f133bae..969eaef9 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -885,7 +885,6 @@
     // TODO(loonybear): Most of this doesn't appear to take into account that
     // each SVGImage gets it's own Page instance.
     GetDeprecation().ClearSuppression();
-    GetVisualViewport().SendUMAMetrics();
     // Need to reset visual viewport position here since before commit load we
     // would update the previous history item, Page::didCommitLoad is called
     // after a new history item is created in FrameLoader.
diff --git a/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc b/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
index 1184d6c..aec01e37 100644
--- a/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
@@ -385,6 +385,12 @@
 
   void TestNonCompositedReasons(const AtomicString& style_class,
                                 const uint32_t reason) {
+    if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled() && reason) {
+      // TODO(crbug.com/1414885): We need a new way to test non-composited
+      // main thread scrolling reasons.
+      return;
+    }
+
     GetFrame()->GetSettings()->SetPreferCompositingToLCDTextForTesting(false);
     Document* document = GetFrame()->GetDocument();
     Element* container = document->getElementById("scroller1");
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
index 4016401f..28ac21b6 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
@@ -235,6 +235,23 @@
 
 INSTANTIATE_PAINT_TEST_SUITE_P(ScrollingTest);
 
+#define ASSERT_COMPOSITED(scroll_node)                            \
+  do {                                                            \
+    ASSERT_TRUE(scroll_node);                                     \
+    ASSERT_TRUE(scroll_node->is_composited);                      \
+    EXPECT_EQ(cc::MainThreadScrollingReason::kNotScrollingOnMain, \
+              scroll_node->main_thread_scrolling_reasons);        \
+  } while (false)
+
+#define ASSERT_NOT_COMPOSITED(scroll_node,                            \
+                              expected_main_thread_scrolling_reasons) \
+  do {                                                                \
+    ASSERT_TRUE(scroll_node);                                         \
+    ASSERT_FALSE(scroll_node->is_composited);                         \
+    EXPECT_EQ(expected_main_thread_scrolling_reasons,                 \
+              scroll_node->main_thread_scrolling_reasons);            \
+  } while (false)
+
 TEST_P(ScrollingTest, fastScrollingByDefault) {
   GetWebView()->MainFrameViewWidget()->Resize(gfx::Size(800, 600));
   LoadHTML("<div id='spacer' style='height: 1000px'></div>");
@@ -248,8 +265,7 @@
   // Fast scrolling should be enabled by default.
   const auto* outer_scroll_node =
       ScrollNodeForScrollableArea(frame_view->LayoutViewport());
-  ASSERT_TRUE(outer_scroll_node);
-  EXPECT_FALSE(outer_scroll_node->main_thread_scrolling_reasons);
+  ASSERT_COMPOSITED(outer_scroll_node);
 
   ASSERT_EQ(cc::EventListenerProperties::kNone,
             LayerTreeHost()->event_listener_properties(
@@ -260,8 +276,7 @@
 
   const auto* inner_scroll_node =
       ScrollNodeForScrollableArea(&page->GetVisualViewport());
-  ASSERT_TRUE(inner_scroll_node);
-  EXPECT_FALSE(inner_scroll_node->main_thread_scrolling_reasons);
+  ASSERT_COMPOSITED(inner_scroll_node);
 }
 
 TEST_P(ScrollingTest, fastFractionalScrollingDiv) {
@@ -312,8 +327,7 @@
   // Sticky position should not fall back to main thread scrolling.
   const auto* scroll_node =
       ScrollNodeForScrollableArea(GetFrame()->View()->LayoutViewport());
-  ASSERT_TRUE(scroll_node);
-  EXPECT_FALSE(scroll_node->main_thread_scrolling_reasons);
+  ASSERT_COMPOSITED(scroll_node);
 
   Document* document = GetFrame()->GetDocument();
   {
@@ -1100,6 +1114,11 @@
 }
 
 TEST_P(ScrollingTest, WheelEventRegionUpdatedOnSubscrollerScrollChange) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1414885): Fix wheel_event_region.
+    return;
+  }
+
   SetPreferCompositingToLCDText(false);
   LoadHTML(R"HTML(
     <style>
@@ -1357,6 +1376,11 @@
 }
 
 TEST_P(ScrollingTest, NonFastScrollableRegionWithBorder) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1414885): Fix non_fast_scrollable_region.
+    return;
+  }
+
   SetPreferCompositingToLCDText(false);
   LoadHTML(R"HTML(
           <!DOCTYPE html>
@@ -1381,6 +1405,11 @@
 }
 
 TEST_P(ScrollingTest, ElementRegionCaptureData) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1414885): Fix this test.
+    return;
+  }
+
   LoadHTML(R"HTML(
               <head>
                 <style type="text/css">
@@ -1576,6 +1605,11 @@
 }
 
 TEST_P(ScrollingTest, NestedIFramesMainThreadScrollingRegion) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1414885): Fix non_fast_scrollable_region.
+    return;
+  }
+
   // This page has an absolute IFRAME. It contains a scrollable child DIV
   // that's nested within an intermediate IFRAME.
   SetPreferCompositingToLCDText(false);
@@ -2111,9 +2145,10 @@
 // noncomposited reasons set. It then removes the box-shadow property and
 // ensures the compositor node updates accordingly.
 TEST_P(UnifiedScrollingSimTest, ScrollNodeForNonCompositedScroller) {
-  // This test requires scroll unification.
-  if (!base::FeatureList::IsEnabled(::features::kScrollUnification))
+  if (!base::FeatureList::IsEnabled(::features::kScrollUnification)) {
+    // This test requires scroll unification.
     return;
+  }
 
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
@@ -2144,19 +2179,14 @@
       MainFrame().GetFrame()->GetDocument()->getElementById("noncomposited");
   auto* scrollable_area =
       noncomposited_element->GetLayoutBoxForScrolling()->GetScrollableArea();
-  ASSERT_EQ(
-      cc::MainThreadScrollingReason::kCantPaintScrollingBackgroundAndLCDText,
-      scrollable_area->GetNonCompositedMainThreadScrollingReasons());
-
   const auto* scroll_node = ScrollNodeForScrollableArea(scrollable_area);
-  ASSERT_TRUE(scroll_node);
-
+  ASSERT_NOT_COMPOSITED(
+      scroll_node,
+      RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()
+          ? cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText
+          : cc::MainThreadScrollingReason::
+                kCantPaintScrollingBackgroundAndLCDText);
   EXPECT_EQ(scroll_node->element_id, scrollable_area->GetScrollElementId());
-  EXPECT_FALSE(RootCcLayer()
-                   ->layer_tree_host()
-                   ->property_trees()
-                   ->scroll_tree()
-                   .IsComposited(*scroll_node));
 
   // Now remove the box-shadow property and ensure the compositor scroll node
   // changes.
@@ -2164,13 +2194,8 @@
                                       "box-shadow: none");
   Compositor().BeginFrame();
 
-  EXPECT_EQ(0u, scrollable_area->GetNonCompositedMainThreadScrollingReasons());
+  ASSERT_COMPOSITED(scroll_node);
   EXPECT_EQ(scroll_node->element_id, scrollable_area->GetScrollElementId());
-  EXPECT_TRUE(RootCcLayer()
-                  ->layer_tree_host()
-                  ->property_trees()
-                  ->scroll_tree()
-                  .IsComposited(*scroll_node));
 }
 
 // Tests that the compositor retains the scroll node for a composited scroller
@@ -2178,9 +2203,10 @@
 // IsComposited state updated accordingly.
 TEST_P(UnifiedScrollingSimTest,
        ScrollNodeForCompositedToNonCompositedScroller) {
-  // This test requires scroll unification.
-  if (!base::FeatureList::IsEnabled(::features::kScrollUnification))
+  if (!base::FeatureList::IsEnabled(::features::kScrollUnification)) {
+    // This test requires scroll unification.
     return;
+  }
 
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
@@ -2210,17 +2236,9 @@
       MainFrame().GetFrame()->GetDocument()->getElementById("composited");
   auto* scrollable_area =
       composited_element->GetLayoutBoxForScrolling()->GetScrollableArea();
-  EXPECT_EQ(0u, scrollable_area->GetNonCompositedMainThreadScrollingReasons());
-
   const auto* scroll_node = ScrollNodeForScrollableArea(scrollable_area);
-  ASSERT_TRUE(scroll_node);
-
+  ASSERT_COMPOSITED(scroll_node);
   EXPECT_EQ(scroll_node->element_id, scrollable_area->GetScrollElementId());
-  EXPECT_TRUE(RootCcLayer()
-                  ->layer_tree_host()
-                  ->property_trees()
-                  ->scroll_tree()
-                  .IsComposited(*scroll_node));
 
   // Now add an inset box-shadow property to make the node noncomposited and
   // ensure the compositor scroll node updates accordingly.
@@ -2228,15 +2246,13 @@
                                    "box-shadow: 10px 10px black inset");
   Compositor().BeginFrame();
 
-  ASSERT_EQ(
-      cc::MainThreadScrollingReason::kCantPaintScrollingBackgroundAndLCDText,
-      scrollable_area->GetNonCompositedMainThreadScrollingReasons());
+  ASSERT_NOT_COMPOSITED(
+      scroll_node,
+      RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()
+          ? cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText
+          : cc::MainThreadScrollingReason::
+                kCantPaintScrollingBackgroundAndLCDText);
   EXPECT_EQ(scroll_node->element_id, scrollable_area->GetScrollElementId());
-  EXPECT_FALSE(RootCcLayer()
-                   ->layer_tree_host()
-                   ->property_trees()
-                   ->scroll_tree()
-                   .IsComposited(*scroll_node));
 }
 
 // Tests that the compositor gets a scroll node for noncomposited scrollers
@@ -2244,9 +2260,14 @@
 // scroller with an inset box shadow, and ensuring that scroller generates a
 // compositor scroll node with the proper noncomposited reasons set.
 TEST_P(UnifiedScrollingSimTest, ScrollNodeForEmbeddedScrollers) {
-  // This test requires scroll unification.
-  if (!base::FeatureList::IsEnabled(::features::kScrollUnification))
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1414885): Fix this test.
     return;
+  }
+  if (!base::FeatureList::IsEnabled(::features::kScrollUnification)) {
+    // This test requires scroll unification.
+    return;
+  }
 
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
@@ -2296,18 +2317,11 @@
       iframe->contentDocument()->View()->LayoutViewport();
   const auto* iframe_scroll_node =
       ScrollNodeForScrollableArea(iframe_scrollable_area);
-  ASSERT_TRUE(iframe_scroll_node);
 
   // The iframe itself is a composited scroller.
-  EXPECT_EQ(
-      0u, iframe_scrollable_area->GetNonCompositedMainThreadScrollingReasons());
+  ASSERT_COMPOSITED(iframe_scroll_node);
   EXPECT_EQ(iframe_scroll_node->element_id,
             iframe_scrollable_area->GetScrollElementId());
-  EXPECT_TRUE(RootCcLayer()
-                  ->layer_tree_host()
-                  ->property_trees()
-                  ->scroll_tree()
-                  .IsComposited(*iframe_scroll_node));
 
   // Ensure we have a compositor scroll node for the noncomposited subscroller.
   auto* child_scrollable_area = iframe->contentDocument()
@@ -2316,19 +2330,14 @@
                                     ->GetScrollableArea();
   const auto* child_scroll_node =
       ScrollNodeForScrollableArea(child_scrollable_area);
-  ASSERT_TRUE(child_scroll_node);
-
-  EXPECT_EQ(
-      cc::MainThreadScrollingReason::kCantPaintScrollingBackgroundAndLCDText,
-      child_scrollable_area->GetNonCompositedMainThreadScrollingReasons());
+  ASSERT_NOT_COMPOSITED(
+      child_scroll_node,
+      RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()
+          ? cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText
+          : cc::MainThreadScrollingReason::
+                kCantPaintScrollingBackgroundAndLCDText);
   EXPECT_EQ(child_scroll_node->element_id,
             child_scrollable_area->GetScrollElementId());
-
-  EXPECT_FALSE(RootCcLayer()
-                   ->layer_tree_host()
-                   ->property_trees()
-                   ->scroll_tree()
-                   .IsComposited(*child_scroll_node));
 }
 
 // Similar to the above test, but for deeper nesting iframes to ensure we
@@ -2406,19 +2415,14 @@
                                     ->GetScrollableArea();
   const auto* child_scroll_node =
       ScrollNodeForScrollableArea(child_scrollable_area);
-  ASSERT_TRUE(child_scroll_node);
-
-  EXPECT_EQ(
-      cc::MainThreadScrollingReason::kCantPaintScrollingBackgroundAndLCDText,
-      child_scrollable_area->GetNonCompositedMainThreadScrollingReasons());
+  ASSERT_NOT_COMPOSITED(
+      child_scroll_node,
+      RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()
+          ? cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText
+          : cc::MainThreadScrollingReason::
+                kCantPaintScrollingBackgroundAndLCDText);
   EXPECT_EQ(child_scroll_node->element_id,
             child_scrollable_area->GetScrollElementId());
-
-  EXPECT_FALSE(RootCcLayer()
-                   ->layer_tree_host()
-                   ->property_trees()
-                   ->scroll_tree()
-                   .IsComposited(*child_scroll_node));
 }
 
 // Tests that the compositor gets a scroll node for opacity 0 noncomposited
@@ -2468,23 +2472,17 @@
 
   // Ensure the opacity 0 noncomposited scrollable area generates a scroll node
   auto* invisible_scrollable_area = ScrollableAreaByDOMElementId("invisible");
-  ASSERT_EQ(
-      cc::MainThreadScrollingReason::kCantPaintScrollingBackgroundAndLCDText,
-      invisible_scrollable_area->GetNonCompositedMainThreadScrollingReasons());
-
   const auto* invisible_scroll_node =
       ScrollNodeForScrollableArea(invisible_scrollable_area);
-  ASSERT_TRUE(invisible_scroll_node);
-
+  ASSERT_NOT_COMPOSITED(
+      invisible_scroll_node,
+      RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()
+          ? cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText
+          : cc::MainThreadScrollingReason::
+                kCantPaintScrollingBackgroundAndLCDText);
   EXPECT_EQ(invisible_scroll_node->element_id,
             invisible_scrollable_area->GetScrollElementId());
 
-  EXPECT_FALSE(RootCcLayer()
-                   ->layer_tree_host()
-                   ->property_trees()
-                   ->scroll_tree()
-                   .IsComposited(*invisible_scroll_node));
-
   // Ensure there's no scrollable area (and therefore no scroll node) for a
   // display none scroller.
   EXPECT_EQ(nullptr, ScrollableAreaByDOMElementId("displaynone"));
@@ -2511,11 +2509,10 @@
   Compositor().BeginFrame();
 
   auto* scrollable_area = ScrollableAreaByDOMElementId("textinput");
-  ASSERT_EQ(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText,
-            scrollable_area->GetNonCompositedMainThreadScrollingReasons());
-
   const auto* scroll_node = ScrollNodeForScrollableArea(scrollable_area);
   ASSERT_TRUE(scroll_node);
+  ASSERT_EQ(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText,
+            scroll_node->main_thread_scrolling_reasons);
 
   EXPECT_EQ(scroll_node->element_id, scrollable_area->GetScrollElementId());
   EXPECT_FALSE(RootCcLayer()
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
index 00f5a79..ca97f9d 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
@@ -2710,7 +2710,14 @@
   LayoutBox* scroller = To<LayoutBox>(bottom_frame.GetDocument()
                                           ->getElementById("scroller")
                                           ->GetLayoutObject());
-  ASSERT_TRUE(scroller->GetScrollableArea()->NeedsCompositedScrolling());
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // In CompositeScrollAfterPaint, NeedsComositedScrolling returns true
+    // only if the scroller is forced to be composited.
+    EXPECT_FALSE(scroller->GetScrollableArea()->NeedsCompositedScrolling());
+  } else {
+    ASSERT_TRUE(scroller->GetScrollableArea()->NeedsCompositedScrolling());
+  }
+
   EXPECT_TRUE(CcLayerByDOMElementId("scroller"));
 
   // Hide the iframes. Scroller should be decomposited.
diff --git a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
index 22480fbf..ab54bb1 100644
--- a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
@@ -404,6 +404,10 @@
 }
 
 TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewResize) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1414885): Fix this test.
+    return;
+  }
   ScopedPreferNonCompositedScrollingForTest non_composited_scrolling(true);
 
   SetBodyInnerHTML(R"HTML(
@@ -480,6 +484,10 @@
 }
 
 TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewGradientResize) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1414885): Fix this test.
+    return;
+  }
   ScopedPreferNonCompositedScrollingForTest non_composited_scrolling(true);
 
   SetBodyInnerHTML(R"HTML(
@@ -644,6 +652,11 @@
 
 TEST_P(PaintAndRasterInvalidationTest,
        NonCompositedBackgroundAttachmentLocalResize) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1414885): Fix this test.
+    return;
+  }
+
   SetUpHTML(*this);
   Element* target = GetDocument().getElementById("target");
   auto* object = target->GetLayoutBox();
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 0bb8e14..497882c 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -81,7 +81,6 @@
 #include "third_party/blink/renderer/core/layout/custom_scrollbar.h"
 #include "third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
-#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h"
@@ -1083,11 +1082,6 @@
           in_overflow_relayout_ = false;
           scrollbar_manager_.DestroyDetachedScrollbars();
         }
-        LayoutObject* parent = GetLayoutBox()->Parent();
-        if (parent && parent->IsFlexibleBox()) {
-          To<LayoutFlexibleBox>(parent)->ClearCachedMainSizeForChild(
-              *GetLayoutBox());
-        }
       }
     }
   } else if (!HasScrollbar() && resizer_will_change) {
@@ -2481,8 +2475,10 @@
       box->ComputeBackgroundPaintLocationIfComposited();
   bool needs_composited_scrolling = ComputeNeedsCompositedScrollingInternal(
       new_background_paint_location, force_prefer_compositing_to_lcd_text);
-  if (!needs_composited_scrolling)
+  if (!RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled() &&
+      !needs_composited_scrolling) {
     new_background_paint_location = kBackgroundPaintInBorderBoxSpace;
+  }
   box->GetMutableForPainting().SetBackgroundPaintLocation(
       new_background_paint_location);
 
@@ -2500,21 +2496,24 @@
   if (CompositingReasonFinder::RequiresCompositingForRootScroller(*layer_)) {
     return true;
   }
-
   if (!ScrollsOverflow()) {
     return false;
   }
-
-  if (!force_prefer_compositing_to_lcd_text &&
-      RuntimeEnabledFeatures::PreferNonCompositedScrollingEnabled()) {
+  if (force_prefer_compositing_to_lcd_text) {
+    return true;
+  }
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // We'll decide composited scrolling in PaintArtifactCompositor later.
+    return false;
+  }
+  if (RuntimeEnabledFeatures::PreferNonCompositedScrollingEnabled()) {
     return false;
   }
 
   const auto* box = GetLayoutBox();
   bool needs_composited_scrolling = true;
-  if (!force_prefer_compositing_to_lcd_text &&
-      box->GetDocument().GetSettings()->GetLCDTextPreference() ==
-          LCDTextPreference::kStronglyPreferred) {
+  if (box->GetDocument().GetSettings()->GetLCDTextPreference() ==
+      LCDTextPreference::kStronglyPreferred) {
     if (!box->TextIsKnownToBeOnOpaqueBackground()) {
       non_composited_main_thread_scrolling_reasons_ |=
           cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText;
@@ -2895,11 +2894,17 @@
 bool PaintLayerScrollableArea::ShouldDirectlyCompositeScrollbar(
     const Scrollbar& scrollbar) const {
   // Don't composite non-scrollable scrollbars.
-  if (!scrollbar.Maximum())
+  if (!scrollbar.Maximum()) {
     return false;
-  if (scrollbar.IsCustomScrollbar())
+  }
+  if (scrollbar.IsCustomScrollbar()) {
     return false;
-
+  }
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1414885): We may composite all scrollbars, or assume they
+    // are before PaintArtifactCompositor::Update().
+    return true;
+  }
   return NeedsCompositedScrolling();
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index 5b5dd5b..50cf3590 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -461,6 +461,10 @@
   // properties which are updated based on the latter.
   bool UsesCompositedScrolling() const override;
 
+  // In CompositeScrollAfterPaint, NeedsCompositedScrolling() is false if
+  // composited scrolling will be determined after paint.
+  // TODO(crbug.com/1414885): We may need to redefine these functions for
+  // CompositeScrollAfterPaint.
   void UpdateNeedsCompositedScrolling(
       bool force_prefer_compositing_to_lcd_text);
   bool NeedsCompositedScrolling() const { return needs_composited_scrolling_; }
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
index 6cc11540..e26a78a 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
@@ -60,31 +60,26 @@
     return *chrome_client_;
   }
 
-  bool IsComposited(const LayoutObject* scroller) {
+  bool HasDirectCompositingReasons(const LayoutObject* scroller) {
     const auto* paint_properties = scroller->FirstFragment().PaintProperties();
     return paint_properties && paint_properties->Transform() &&
            paint_properties->Transform()->HasDirectCompositingReasons();
   }
 
-  bool UsesCompositedScrolling(const LayoutObject* scroller) {
-    const auto* paint_properties = scroller->FirstFragment().PaintProperties();
-    bool composited =
-        paint_properties && paint_properties->ScrollTranslation() &&
-        paint_properties->ScrollTranslation()->HasDirectCompositingReasons();
-
-    auto* layer = To<LayoutBoxModelObject>(scroller)->Layer();
-    if (!layer) {
-      DCHECK(!composited);
-      return false;
+  bool UsesCompositedScrolling(const LayoutBox* scroller) {
+    // TODO(crbug.com/1414885): In CompositeScrollAfterPaint, the tests no
+    // longer test PaintLayerScrollableArea. We should probably move them into
+    // scrolling_test.cc.
+    if (auto* scrollable_area = scroller->GetScrollableArea()) {
+      const auto* property_trees =
+          GetFrame().View()->RootCcLayer()->layer_tree_host()->property_trees();
+      if (const auto* scroll_node =
+              property_trees->scroll_tree().FindNodeFromElementId(
+                  scrollable_area->GetScrollElementId())) {
+        return scroll_node->is_composited;
+      }
     }
-
-    auto* scrollable_area = layer->GetScrollableArea();
-    if (!scrollable_area) {
-      DCHECK(!composited);
-      return false;
-    }
-
-    return composited;
+    return false;
   }
 
  private:
@@ -109,8 +104,7 @@
     <div id="scroller"><div id="scrolled"></div></div>
   )HTML");
 
-  auto* scroller = GetLayoutObjectByElementId("scroller");
-  EXPECT_TRUE(UsesCompositedScrolling(scroller));
+  EXPECT_TRUE(UsesCompositedScrolling(GetLayoutBoxByElementId("scroller")));
 }
 
 TEST_P(MAYBE_PaintLayerScrollableAreaTest, NonStackingContextScrollerPromoted) {
@@ -128,7 +122,7 @@
     </div>
   )HTML");
 
-  EXPECT_TRUE(UsesCompositedScrolling(GetLayoutObjectByElementId("scroller")));
+  EXPECT_TRUE(UsesCompositedScrolling(GetLayoutBoxByElementId("scroller")));
 }
 
 TEST_P(MAYBE_PaintLayerScrollableAreaTest, TransparentLayersNotPromoted) {
@@ -142,7 +136,7 @@
     <div id="scroller"><div id="scrolled"></div></div>
   )HTML");
 
-  EXPECT_FALSE(UsesCompositedScrolling(GetLayoutObjectByElementId("scroller")));
+  EXPECT_FALSE(UsesCompositedScrolling(GetLayoutBoxByElementId("scroller")));
 }
 
 TEST_P(MAYBE_PaintLayerScrollableAreaTest,
@@ -157,14 +151,14 @@
   )HTML");
 
   Element* scroller = GetDocument().getElementById("scroller");
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   // Change the background to transparent
   scroller->setAttribute(
       html_names::kStyleAttr,
       "background: rgba(255,255,255,0.5) local content-box;");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_FALSE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_FALSE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 }
 
 TEST_P(MAYBE_PaintLayerScrollableAreaTest, OpaqueLayersPromotedOnStyleChange) {
@@ -178,13 +172,13 @@
   )HTML");
 
   Element* scroller = GetDocument().getElementById("scroller");
-  EXPECT_FALSE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_FALSE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   // Change the background to opaque
   scroller->setAttribute(html_names::kStyleAttr,
                          "background: white local content-box;");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 }
 
 // Tests that a transform on the scroller or an ancestor doesn't prevent
@@ -204,23 +198,23 @@
 
   Element* parent = GetDocument().getElementById("parent");
   Element* scroller = GetDocument().getElementById("scroller");
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   // Change the parent to have a transform.
   parent->setAttribute(html_names::kStyleAttr, "transform: translate(1px, 0);");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   // Change the parent to have no transform again.
   parent->removeAttribute(html_names::kStyleAttr);
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   // Apply a transform to the scroller directly.
   scroller->setAttribute(html_names::kStyleAttr,
                          "transform: translate(1px, 0);");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 }
 
 TEST_P(MAYBE_PaintLayerScrollableAreaTest,
@@ -238,22 +232,22 @@
 
   Element* parent = GetDocument().getElementById("parent");
   Element* scroller = GetDocument().getElementById("scroller");
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   // Change the parent to be partially translucent.
   parent->setAttribute(html_names::kStyleAttr, "opacity: 0.5;");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   // Change the parent to be opaque again.
   parent->setAttribute(html_names::kStyleAttr, "opacity: 1;");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   // Make the scroller translucent.
   scroller->setAttribute(html_names::kStyleAttr, "opacity: 0.5");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 }
 
 // Test that will-change: transform applied to the scroller will cause the
@@ -269,15 +263,15 @@
   )HTML");
 
   Element* scroller = GetDocument().getElementById("scroller");
-  EXPECT_FALSE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_FALSE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   scroller->setAttribute(html_names::kStyleAttr, "will-change: transform");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   scroller->setAttribute(html_names::kStyleAttr, "");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_FALSE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_FALSE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 }
 
 // Test that will-change: transform applied to the scroller will cause the
@@ -293,24 +287,24 @@
   )HTML");
 
   Element* scroller = GetDocument().getElementById("scroller");
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   // pointer-events: none does not affect whether composited scrolling is
   // present.
   scroller->setAttribute(html_names::kStyleAttr, "pointer-events: none");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   // visibility: hidden causes the scroller to be invisible for hit testing,
   // so ScrollsOverflow becomes false on the PaintLayerScrollableArea, and hence
   // composited scrolling is not present.
   scroller->setAttribute(html_names::kStyleAttr, "visibility: hidden");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_FALSE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_FALSE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 
   scroller->setAttribute(html_names::kStyleAttr, "");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(scroller->GetLayoutBox()));
 }
 
 // Test that <input> elements don't use composited scrolling even with
@@ -325,13 +319,13 @@
   )HTML");
 
   Element* element = GetDocument().getElementById("input");
-  EXPECT_FALSE(IsComposited(element->GetLayoutObject()));
-  EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutObject()));
+  EXPECT_FALSE(HasDirectCompositingReasons(element->GetLayoutObject()));
+  EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutBox()));
 
   element->setAttribute("class", "composited");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(IsComposited(element->GetLayoutObject()));
-  EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutObject()));
+  EXPECT_TRUE(HasDirectCompositingReasons(element->GetLayoutObject()));
+  EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutBox()));
 }
 
 // Test that <select> elements use composited scrolling with
@@ -351,17 +345,29 @@
   )HTML");
 
   Element* element = GetDocument().getElementById("select");
-  EXPECT_FALSE(IsComposited(element->GetLayoutObject()));
-  EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutObject()));
+  EXPECT_FALSE(HasDirectCompositingReasons(element->GetLayoutObject()));
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // PaintArtifactCompositor can detect the opaque background of <select>
+    // and use composited scrolling.
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+    // <select> implementation is different and not scrollable on Android and
+    // iOS.
+    EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutBox()));
+#else
+    EXPECT_TRUE(UsesCompositedScrolling(element->GetLayoutBox()));
+#endif
+  } else {
+    EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutBox()));
+  }
 
   element->setAttribute("class", "composited");
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(IsComposited(element->GetLayoutObject()));
+  EXPECT_TRUE(HasDirectCompositingReasons(element->GetLayoutBox()));
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
   // <select> implementation is different and not scrollable on Android and iOS.
-  EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutObject()));
+  EXPECT_FALSE(UsesCompositedScrolling(element->GetLayoutBox()));
 #else
-  EXPECT_TRUE(UsesCompositedScrolling(element->GetLayoutObject()));
+  EXPECT_TRUE(UsesCompositedScrolling(element->GetLayoutBox()));
 #endif
 }
 
@@ -492,8 +498,7 @@
 
   Element* scroller = GetDocument().getElementById("scroller");
   PaintLayerScrollableArea* scrollable_area =
-      To<LayoutBoxModelObject>(scroller->GetLayoutObject())
-          ->GetScrollableArea();
+      scroller->GetLayoutBox()->GetScrollableArea();
   ASSERT_TRUE(scrollable_area);
 
   EXPECT_CALL(GetChromeClient(), MockUpdateTooltipUnderCursor(
@@ -526,8 +531,7 @@
   Element* scroller = GetDocument().getElementById("scroller");
   ASSERT_TRUE(scroller);
   PaintLayerScrollableArea* scrollable_area =
-      To<LayoutBoxModelObject>(scroller->GetLayoutObject())
-          ->GetScrollableArea();
+      scroller->GetLayoutBox()->GetScrollableArea();
   ASSERT_TRUE(scrollable_area);
   scrollable_area->SetScrollOffset(ScrollOffset(100, 0),
                                    mojom::blink::ScrollType::kClamping);
@@ -558,11 +562,10 @@
 
   Element* outer_div = GetDocument().getElementById("outerDiv");
   ASSERT_TRUE(outer_div);
-  outer_div->GetLayoutObject()->SetNeedsLayout("test");
+  outer_div->GetLayoutBox()->SetNeedsLayout("test");
   UpdateAllLifecyclePhasesForTest();
   PaintLayerScrollableArea* scrollable_area =
-      To<LayoutBoxModelObject>(outer_div->GetLayoutObject())
-          ->GetScrollableArea();
+      outer_div->GetLayoutBox()->GetScrollableArea();
   ASSERT_TRUE(scrollable_area);
   EXPECT_TRUE(scrollable_area->HasVerticalScrollbar());
 }
@@ -590,8 +593,7 @@
   Element* container = GetDocument().getElementById("container");
   ASSERT_TRUE(container);
   PaintLayerScrollableArea* scrollable_area =
-      To<LayoutBoxModelObject>(container->GetLayoutObject())
-          ->GetScrollableArea();
+      container->GetLayoutBox()->GetScrollableArea();
   ASSERT_TRUE(scrollable_area);
   EXPECT_FALSE(scrollable_area->HasHorizontalScrollbar());
 }
@@ -619,8 +621,7 @@
   Element* container = GetDocument().getElementById("container");
   ASSERT_TRUE(container);
   PaintLayerScrollableArea* scrollable_area =
-      To<LayoutBoxModelObject>(container->GetLayoutObject())
-          ->GetScrollableArea();
+      container->GetLayoutBox()->GetScrollableArea();
   ASSERT_TRUE(scrollable_area);
   EXPECT_EQ(scrollable_area->ScrollOrigin().x(), 100);
 }
@@ -640,7 +641,7 @@
     </div>
   )HTML");
 
-  auto* scroller = GetLayoutObjectByElementId("scroller");
+  auto* scroller = GetLayoutBoxByElementId("scroller");
   auto* scrollable_area =
       To<LayoutBoxModelObject>(scroller)->GetScrollableArea();
 
@@ -701,7 +702,7 @@
     </div>
   )HTML");
 
-  auto* scroller = GetLayoutObjectByElementId("scroller");
+  auto* scroller = GetLayoutBoxByElementId("scroller");
   auto* scrollable_area =
       To<LayoutBoxModelObject>(scroller)->GetScrollableArea();
 
@@ -795,6 +796,11 @@
 
 TEST_P(MAYBE_PaintLayerScrollableAreaTest,
        ScrollWithLocalAttachmentBackgroundInScrollingContents) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1414885): Fix this test.
+    return;
+  }
+
   SetBodyInnerHTML(R"HTML(
     <style>
       #scroller {
@@ -816,7 +822,11 @@
             scroller->ComputeBackgroundPaintLocationIfComposited());
   EXPECT_EQ(kBackgroundPaintInContentsSpace,
             scroller->GetBackgroundPaintLocation());
-  EXPECT_TRUE(UsesCompositedScrolling(scroller));
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    EXPECT_FALSE(UsesCompositedScrolling(scroller));
+  } else {
+    EXPECT_TRUE(UsesCompositedScrolling(scroller));
+  }
 
   // Programmatically changing the scroll offset.
   scrollable_area->SetScrollOffset(ScrollOffset(0, 1),
@@ -1071,7 +1081,7 @@
     <div id='scroller'><div id='scrolled'></div></div>
   )HTML");
 
-  auto* scroller = GetLayoutObjectByElementId("scroller");
+  auto* scroller = GetLayoutBoxByElementId("scroller");
   auto* scrollable_area =
       To<LayoutBoxModelObject>(scroller)->GetScrollableArea();
 
@@ -1119,7 +1129,7 @@
     <div id='scroller'><div id='scrolled'></div></div>
   )HTML");
 
-  auto* scroller = GetLayoutObjectByElementId("scroller");
+  auto* scroller = GetLayoutBoxByElementId("scroller");
   auto* scrollable_area =
       To<LayoutBoxModelObject>(scroller)->GetScrollableArea();
 
@@ -1147,10 +1157,9 @@
       <div style="width: 10px; height: 700px; background: lightblue"></div>
     </div>
   )HTML");
-  auto* scroller =
-      To<LayoutBoxModelObject>(GetLayoutObjectByElementId("scroller"));
+  auto* scroller = GetLayoutBoxByElementId("scroller");
   auto* scrollable_area = scroller->GetScrollableArea();
-  auto* sticky = To<LayoutBoxModelObject>(GetLayoutObjectByElementId("sticky"));
+  auto* sticky = GetLayoutBoxByElementId("sticky");
 
   EXPECT_EQ(&sticky->FirstFragment().LocalBorderBoxProperties().Transform(),
             sticky->FirstFragment().PaintProperties()->StickyTranslation());
@@ -1381,7 +1390,7 @@
     </div>
   )HTML");
 
-  EXPECT_TRUE(UsesCompositedScrolling(GetLayoutObjectByElementId("scroller")));
+  EXPECT_TRUE(UsesCompositedScrolling(GetLayoutBoxByElementId("scroller")));
 }
 
 class PaintLayerScrollableAreaTestLowEndPlatform
@@ -1413,7 +1422,7 @@
     </div>
   )HTML");
 
-  EXPECT_TRUE(UsesCompositedScrolling(GetLayoutObjectByElementId("scroller")));
+  EXPECT_TRUE(UsesCompositedScrolling(GetLayoutBoxByElementId("scroller")));
 }
 
 TEST_P(MAYBE_PaintLayerScrollableAreaTest, SetSnapContainerDataNeedsUpdate) {
@@ -1433,11 +1442,11 @@
     </div>
   )HTML");
 
-  auto* first_scroller = GetLayoutObjectByElementId("first_scroller");
+  auto* first_scroller = GetLayoutBoxByElementId("first_scroller");
   auto* first_scrollable_area =
       To<LayoutBoxModelObject>(first_scroller)->GetScrollableArea();
 
-  auto* second_scroller = GetLayoutObjectByElementId("second_scroller");
+  auto* second_scroller = GetLayoutBoxByElementId("second_scroller");
   auto* second_scrollable_area =
       To<LayoutBoxModelObject>(second_scroller)->GetScrollableArea();
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_test.cc b/third_party/blink/renderer/core/paint/paint_layer_test.cc
index 980771c4..5b132e3 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_test.cc
@@ -121,7 +121,7 @@
   PaintLayer* content_layer = GetPaintLayerByElementId("content");
   const auto& fragment = content_layer->GetLayoutObject().FirstFragment();
   EXPECT_EQ(PhysicalOffset(), content_layer->LocationWithoutPositionOffset());
-  if (RuntimeEnabledFeatures::UnifiedScrollPaintingEnabled()) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
     EXPECT_EQ(gfx::Rect(0, 0, 2000, 2000),
               fragment.GetContentsCullRect().Rect());
   } else {
@@ -138,7 +138,7 @@
       content_layer->ContainingLayer()->PixelSnappedScrolledContentOffset());
 
   EXPECT_FALSE(scroll_layer->SelfNeedsRepaint());
-  if (RuntimeEnabledFeatures::UnifiedScrollPaintingEnabled()) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
     EXPECT_EQ(gfx::Rect(0, 0, 2000, 2000),
               fragment.GetContentsCullRect().Rect());
     EXPECT_FALSE(content_layer->SelfNeedsRepaint());
diff --git a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector_test.cc
index c60da0d..23cd062 100644
--- a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector_test.cc
+++ b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector_test.cc
@@ -1241,8 +1241,10 @@
 
 TEST_P(ImagePaintTimingDetectorTest, LargestImagePaint_Detached_Frame) {
 #if BUILDFLAG(IS_ANDROID)
-  if (RuntimeEnabledFeatures::SolidColorLayersEnabled()) {
-    // TODO(crbug.com/1353921): This test is flaky on Android. Fix it.
+  if (RuntimeEnabledFeatures::SolidColorLayersEnabled() ||
+      RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    // TODO(crbug.com/1353921, crbug.com/1414885):
+    // This test is flaky on Android. Fix it.
     // https://chrome-swarming.appspot.com/task?id=60c68038be22f011
     // The first EXPECT_EQ(0u, events.size()) below failed.
     return;
diff --git a/third_party/blink/renderer/core/style/build.gni b/third_party/blink/renderer/core/style/build.gni
index c174c385..a88e867 100644
--- a/third_party/blink/renderer/core/style/build.gni
+++ b/third_party/blink/renderer/core/style/build.gni
@@ -39,8 +39,6 @@
   "grid_enums.h",
   "grid_length.h",
   "grid_position.h",
-  "grid_positions_resolver.cc",
-  "grid_positions_resolver.h",
   "grid_track_list.cc",
   "grid_track_list.h",
   "grid_track_size.h",
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 0a98265f..31ee4653 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -95,6 +95,7 @@
 class FilterOperations;
 class Font;
 class Hyphenation;
+class LayoutBox;
 class LayoutTheme;
 class Longhand;
 class NinePieceImage;
diff --git a/third_party/blink/renderer/core/style/grid_area.h b/third_party/blink/renderer/core/style/grid_area.h
index 71b46a0..417a611 100644
--- a/third_party/blink/renderer/core/style/grid_area.h
+++ b/third_party/blink/renderer/core/style/grid_area.h
@@ -33,7 +33,7 @@
 
 #include "base/check_op.h"
 #include "base/dcheck_is_on.h"
-#include "third_party/blink/renderer/core/style/grid_positions_resolver.h"
+#include "third_party/blink/renderer/core/style/grid_enums.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
diff --git a/third_party/blink/renderer/core/style/grid_positions_resolver.cc b/third_party/blink/renderer/core/style/grid_positions_resolver.cc
deleted file mode 100644
index 09f941b..0000000
--- a/third_party/blink/renderer/core/style/grid_positions_resolver.cc
+++ /dev/null
@@ -1,577 +0,0 @@
-// Copyright 2014 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/style/grid_positions_resolver.h"
-
-#include <algorithm>
-#include "third_party/blink/renderer/core/style/computed_style.h"
-#include "third_party/blink/renderer/core/style/grid_area.h"
-
-namespace blink {
-
-static inline GridTrackSizingDirection DirectionFromSide(
-    GridPositionSide side) {
-  return side == kColumnStartSide || side == kColumnEndSide ? kForColumns
-                                                            : kForRows;
-}
-
-static inline String ImplicitNamedGridLineForSide(const String& line_name,
-                                                  GridPositionSide side) {
-  return line_name + ((side == kColumnStartSide || side == kRowStartSide)
-                          ? "-start"
-                          : "-end");
-}
-
-NamedLineCollection::NamedLineCollection(
-    const ComputedStyle& grid_container_style,
-    const String& named_line,
-    GridTrackSizingDirection track_direction,
-    wtf_size_t last_line,
-    wtf_size_t auto_repeat_tracks_count,
-    bool is_subgridded_to_parent)
-    : last_line_(last_line),
-      auto_repeat_total_tracks_(auto_repeat_tracks_count) {
-  const bool is_for_columns = track_direction == kForColumns;
-  const ComputedGridTrackList& computed_grid_track_list =
-      is_for_columns ? grid_container_style.GridTemplateColumns()
-                     : grid_container_style.GridTemplateRows();
-  is_standalone_grid_ =
-      computed_grid_track_list.axis_type == GridAxisType::kStandaloneAxis;
-
-  // Line names from the container style are valid when the grid axis type is a
-  // standalone grid or the axis is a subgrid and the parent is a grid. See:
-  // https://www.w3.org/TR/css-grid-2/#subgrid-listing
-  bool are_named_lines_valid = true;
-  if (RuntimeEnabledFeatures::LayoutNGSubgridEnabled()) {
-    are_named_lines_valid = is_subgridded_to_parent || is_standalone_grid_;
-  }
-
-  const NamedGridLinesMap& grid_line_names =
-      computed_grid_track_list.named_grid_lines;
-  const NamedGridLinesMap& auto_repeat_grid_line_names =
-      computed_grid_track_list.auto_repeat_named_grid_lines;
-  const NamedGridLinesMap& implicit_grid_line_names =
-      is_for_columns ? grid_container_style.ImplicitNamedGridColumnLines()
-                     : grid_container_style.ImplicitNamedGridRowLines();
-
-  if (!grid_line_names.empty() && are_named_lines_valid) {
-    auto it = grid_line_names.find(named_line);
-    named_lines_indexes_ = it == grid_line_names.end() ? nullptr : &it->value;
-  }
-
-  if (!auto_repeat_grid_line_names.empty() && are_named_lines_valid) {
-    auto it = auto_repeat_grid_line_names.find(named_line);
-    auto_repeat_named_lines_indexes_ =
-        it == auto_repeat_grid_line_names.end() ? nullptr : &it->value;
-  }
-
-  if (!implicit_grid_line_names.empty()) {
-    auto it = implicit_grid_line_names.find(named_line);
-    implicit_named_lines_indexes_ =
-        it == implicit_grid_line_names.end() ? nullptr : &it->value;
-  }
-
-  insertion_point_ = computed_grid_track_list.auto_repeat_insertion_point;
-  auto_repeat_track_list_length_ =
-      computed_grid_track_list.auto_repeat_track_sizes.size();
-}
-
-bool NamedLineCollection::HasExplicitNamedLines() {
-  return named_lines_indexes_ || auto_repeat_named_lines_indexes_;
-}
-
-bool NamedLineCollection::HasNamedLines() {
-  return HasExplicitNamedLines() || implicit_named_lines_indexes_;
-}
-
-bool NamedLineCollection::Contains(wtf_size_t line) {
-  CHECK(HasNamedLines());
-
-  if (line > last_line_) {
-    return false;
-  }
-
-  auto find = [](const Vector<wtf_size_t>* indexes, wtf_size_t line) {
-    return indexes && indexes->Find(line) != kNotFound;
-  };
-
-  if (find(implicit_named_lines_indexes_, line)) {
-    return true;
-  }
-
-  if (auto_repeat_track_list_length_ == 0 || line < insertion_point_) {
-    return find(named_lines_indexes_, line);
-  }
-
-  DCHECK(auto_repeat_total_tracks_);
-
-  if (line > insertion_point_ + auto_repeat_total_tracks_) {
-    return find(named_lines_indexes_, line - (auto_repeat_total_tracks_ - 1));
-  }
-
-  if (line == insertion_point_) {
-    return find(named_lines_indexes_, line) ||
-           find(auto_repeat_named_lines_indexes_, 0);
-  }
-
-  if (line == insertion_point_ + auto_repeat_total_tracks_) {
-    return find(auto_repeat_named_lines_indexes_,
-                auto_repeat_track_list_length_) ||
-           find(named_lines_indexes_, insertion_point_ + 1);
-  }
-
-  wtf_size_t auto_repeat_index_in_first_repetition =
-      (line - insertion_point_) % auto_repeat_track_list_length_;
-  if (!auto_repeat_index_in_first_repetition &&
-      find(auto_repeat_named_lines_indexes_, auto_repeat_track_list_length_)) {
-    return true;
-  }
-  return find(auto_repeat_named_lines_indexes_,
-              auto_repeat_index_in_first_repetition);
-}
-
-wtf_size_t NamedLineCollection::FirstExplicitPosition() {
-  DCHECK(HasExplicitNamedLines());
-
-  wtf_size_t first_line = 0;
-
-  // If it is an standalone grid and there is no auto repeat(), there must be
-  // some named line outside, return the 1st one. Also return it if it precedes
-  // the auto-repeat().
-  if ((is_standalone_grid_ && auto_repeat_track_list_length_ == 0) ||
-      (named_lines_indexes_ &&
-       named_lines_indexes_->at(first_line) <= insertion_point_)) {
-    return named_lines_indexes_->at(first_line);
-  }
-
-  // Return the 1st named line inside the auto repeat(), if any.
-  if (auto_repeat_named_lines_indexes_) {
-    return auto_repeat_named_lines_indexes_->at(first_line) + insertion_point_;
-  }
-
-  // The 1st named line must be after the auto repeat().
-  return named_lines_indexes_->at(first_line) + auto_repeat_total_tracks_ - 1;
-}
-
-wtf_size_t NamedLineCollection::FirstPosition() {
-  CHECK(HasNamedLines());
-
-  if (!implicit_named_lines_indexes_) {
-    return FirstExplicitPosition();
-  }
-
-  wtf_size_t first_line = 0;
-  if (!HasExplicitNamedLines()) {
-    return implicit_named_lines_indexes_->at(first_line);
-  }
-
-  return std::min(FirstExplicitPosition(),
-                  implicit_named_lines_indexes_->at(first_line));
-}
-
-static void InitialAndFinalPositionsFromStyle(
-    const ComputedStyle& grid_item_style,
-    GridTrackSizingDirection track_direction,
-    GridPosition& initial_position,
-    GridPosition& final_position) {
-  const bool is_for_columns = track_direction == kForColumns;
-  initial_position = is_for_columns ? grid_item_style.GridColumnStart()
-                                    : grid_item_style.GridRowStart();
-  final_position = is_for_columns ? grid_item_style.GridColumnEnd()
-                                  : grid_item_style.GridRowEnd();
-
-  // We must handle the placement error handling code here instead of in the
-  // StyleAdjuster because we don't want to overwrite the specified values.
-  if (initial_position.IsSpan() && final_position.IsSpan()) {
-    final_position.SetAutoPosition();
-  }
-
-  // If the grid item has an automatic position and a grid span for a named line
-  // in a given dimension, instead treat the grid span as one.
-  if (initial_position.IsAuto() && final_position.IsSpan() &&
-      !final_position.NamedGridLine().IsNull()) {
-    final_position.SetSpanPosition(1, g_null_atom);
-  }
-  if (final_position.IsAuto() && initial_position.IsSpan() &&
-      !initial_position.NamedGridLine().IsNull()) {
-    initial_position.SetSpanPosition(1, g_null_atom);
-  }
-}
-
-static wtf_size_t LookAheadForNamedGridLine(
-    int start,
-    wtf_size_t number_of_lines,
-    wtf_size_t grid_last_line,
-    NamedLineCollection& lines_collection) {
-  DCHECK(number_of_lines);
-
-  // Only implicit lines on the search direction are assumed to have the given
-  // name, so we can start to look from first line.
-  // See: https://drafts.csswg.org/css-grid/#grid-placement-span-int
-  wtf_size_t end = std::max(start, 0);
-
-  if (!lines_collection.HasNamedLines()) {
-    end = std::max(end, grid_last_line + 1);
-    return end + number_of_lines - 1;
-  }
-
-  for (; number_of_lines; ++end) {
-    if (end > grid_last_line || lines_collection.Contains(end)) {
-      number_of_lines--;
-    }
-  }
-
-  DCHECK(end);
-  return end - 1;
-}
-
-static int LookBackForNamedGridLine(int end,
-                                    wtf_size_t number_of_lines,
-                                    int grid_last_line,
-                                    NamedLineCollection& lines_collection) {
-  DCHECK(number_of_lines);
-
-  // Only implicit lines on the search direction are assumed to have the given
-  // name, so we can start to look from last line.
-  // See: https://drafts.csswg.org/css-grid/#grid-placement-span-int
-  int start = std::min(end, grid_last_line);
-
-  if (!lines_collection.HasNamedLines()) {
-    start = std::min(start, -1);
-    return start - number_of_lines + 1;
-  }
-
-  for (; number_of_lines; --start) {
-    if (start < 0 || lines_collection.Contains(start)) {
-      number_of_lines--;
-    }
-  }
-
-  return start + 1;
-}
-
-static GridSpan DefiniteGridSpanWithNamedSpanAgainstOpposite(
-    int opposite_line,
-    const GridPosition& position,
-    GridPositionSide side,
-    int last_line,
-    NamedLineCollection& lines_collection) {
-  int start, end;
-  if (side == kRowStartSide || side == kColumnStartSide) {
-    start = LookBackForNamedGridLine(opposite_line - 1, position.SpanPosition(),
-                                     last_line, lines_collection);
-    end = opposite_line;
-  } else {
-    start = opposite_line;
-    end = LookAheadForNamedGridLine(opposite_line + 1, position.SpanPosition(),
-                                    last_line, lines_collection);
-  }
-
-  return GridSpan::UntranslatedDefiniteGridSpan(start, end);
-}
-
-wtf_size_t GridPositionsResolver::ExplicitGridColumnCount(
-    const ComputedStyle& grid_container_style,
-    wtf_size_t auto_repeat_tracks_count,
-    wtf_size_t subgrid_span_size) {
-  if (subgrid_span_size != kNotFound) {
-    return subgrid_span_size;
-  }
-
-  const auto& track_list =
-      grid_container_style.GridTemplateColumns().track_sizes;
-  const wtf_size_t total_track_count = track_list.LegacyTrackList().size();
-
-  return std::min<wtf_size_t>(
-      std::max(total_track_count + auto_repeat_tracks_count,
-               grid_container_style.NamedGridAreaColumnCount()),
-      kGridMaxTracks);
-}
-
-wtf_size_t GridPositionsResolver::ExplicitGridRowCount(
-    const ComputedStyle& grid_container_style,
-    wtf_size_t auto_repeat_tracks_count,
-    wtf_size_t subgrid_span_size) {
-  if (subgrid_span_size != kNotFound) {
-    return subgrid_span_size;
-  }
-
-  const auto& track_list = grid_container_style.GridTemplateRows().track_sizes;
-  const wtf_size_t total_track_count = track_list.LegacyTrackList().size();
-
-  return std::min<wtf_size_t>(
-      std::max(total_track_count + auto_repeat_tracks_count,
-               grid_container_style.NamedGridAreaRowCount()),
-      kGridMaxTracks);
-}
-
-static wtf_size_t ExplicitGridSizeForSide(
-    const ComputedStyle& grid_container_style,
-    GridPositionSide side,
-    wtf_size_t auto_repeat_tracks_count,
-    wtf_size_t subgrid_span_size) {
-  return (side == kColumnStartSide || side == kColumnEndSide)
-             ? GridPositionsResolver::ExplicitGridColumnCount(
-                   grid_container_style, auto_repeat_tracks_count,
-                   subgrid_span_size)
-             : GridPositionsResolver::ExplicitGridRowCount(
-                   grid_container_style, auto_repeat_tracks_count,
-                   subgrid_span_size);
-}
-
-static GridSpan ResolveNamedGridLinePositionAgainstOppositePosition(
-    const ComputedStyle& grid_container_style,
-    int opposite_line,
-    const GridPosition& position,
-    wtf_size_t auto_repeat_tracks_count,
-    GridPositionSide side,
-    wtf_size_t subgrid_span_size) {
-  DCHECK(position.IsSpan());
-  DCHECK(!position.NamedGridLine().IsNull());
-  // Negative positions are not allowed per the specification and should have
-  // been handled during parsing.
-  DCHECK_GT(position.SpanPosition(), 0);
-
-  wtf_size_t last_line = ExplicitGridSizeForSide(
-      grid_container_style, side, auto_repeat_tracks_count, subgrid_span_size);
-  NamedLineCollection lines_collection(
-      grid_container_style, position.NamedGridLine(), DirectionFromSide(side),
-      last_line, auto_repeat_tracks_count);
-  return DefiniteGridSpanWithNamedSpanAgainstOpposite(
-      opposite_line, position, side, last_line, lines_collection);
-}
-
-static GridSpan DefiniteGridSpanWithSpanAgainstOpposite(
-    int opposite_line,
-    const GridPosition& position,
-    GridPositionSide side) {
-  wtf_size_t position_offset = position.SpanPosition();
-  if (side == kColumnStartSide || side == kRowStartSide) {
-    return GridSpan::UntranslatedDefiniteGridSpan(
-        opposite_line - position_offset, opposite_line);
-  }
-
-  return GridSpan::UntranslatedDefiniteGridSpan(
-      opposite_line, opposite_line + position_offset);
-}
-
-static GridSpan ResolveGridPositionAgainstOppositePosition(
-    const ComputedStyle& grid_container_style,
-    int opposite_line,
-    const GridPosition& position,
-    GridPositionSide side,
-    wtf_size_t auto_repeat_tracks_count,
-    wtf_size_t subgrid_span_size) {
-  if (position.IsAuto()) {
-    if (side == kColumnStartSide || side == kRowStartSide) {
-      return GridSpan::UntranslatedDefiniteGridSpan(opposite_line - 1,
-                                                    opposite_line);
-    }
-    return GridSpan::UntranslatedDefiniteGridSpan(opposite_line,
-                                                  opposite_line + 1);
-  }
-
-  DCHECK(position.IsSpan());
-  DCHECK_GT(position.SpanPosition(), 0);
-
-  if (!position.NamedGridLine().IsNull()) {
-    // span 2 'c' -> we need to find the appropriate grid line before / after
-    // our opposite position.
-    return ResolveNamedGridLinePositionAgainstOppositePosition(
-        grid_container_style, opposite_line, position, auto_repeat_tracks_count,
-        side, subgrid_span_size);
-  }
-
-  return DefiniteGridSpanWithSpanAgainstOpposite(opposite_line, position, side);
-}
-
-static wtf_size_t SpanSizeFromPositions(const GridPosition& initial_position,
-                                        const GridPosition& final_position) {
-  // This method will only be used when both positions need to be resolved
-  // against the opposite one.
-  DCHECK(initial_position.ShouldBeResolvedAgainstOppositePosition() &&
-         final_position.ShouldBeResolvedAgainstOppositePosition());
-
-  if (initial_position.IsAuto() && final_position.IsAuto()) {
-    return 1;
-  }
-
-  const GridPosition& span_position =
-      initial_position.IsSpan() ? initial_position : final_position;
-  DCHECK(span_position.IsSpan() && span_position.SpanPosition());
-  return span_position.SpanPosition();
-}
-
-wtf_size_t GridPositionsResolver::SpanSizeForAutoPlacedItem(
-    const ComputedStyle& grid_item_style,
-    GridTrackSizingDirection track_direction) {
-  GridPosition initial_position, final_position;
-  InitialAndFinalPositionsFromStyle(grid_item_style, track_direction,
-                                    initial_position, final_position);
-  return SpanSizeFromPositions(initial_position, final_position);
-}
-
-static int ResolveNamedGridLinePositionFromStyle(
-    const ComputedStyle& grid_container_style,
-    const GridPosition& position,
-    GridPositionSide side,
-    wtf_size_t auto_repeat_tracks_count,
-    wtf_size_t subgrid_span_size) {
-  DCHECK(!position.NamedGridLine().IsNull());
-
-  wtf_size_t last_line = ExplicitGridSizeForSide(
-      grid_container_style, side, auto_repeat_tracks_count, subgrid_span_size);
-  NamedLineCollection lines_collection(
-      grid_container_style, position.NamedGridLine(), DirectionFromSide(side),
-      last_line, auto_repeat_tracks_count);
-
-  if (position.IsPositive()) {
-    return LookAheadForNamedGridLine(0, abs(position.IntegerPosition()),
-                                     last_line, lines_collection);
-  }
-
-  return LookBackForNamedGridLine(last_line, abs(position.IntegerPosition()),
-                                  last_line, lines_collection);
-}
-
-static int ResolveGridPositionFromStyle(
-    const ComputedStyle& grid_container_style,
-    const GridPosition& position,
-    GridPositionSide side,
-    wtf_size_t auto_repeat_tracks_count,
-    bool is_subgridded_to_parent,
-    wtf_size_t subgrid_span_size) {
-  switch (position.GetType()) {
-    case kExplicitPosition: {
-      DCHECK(position.IntegerPosition());
-
-      if (!position.NamedGridLine().IsNull()) {
-        return ResolveNamedGridLinePositionFromStyle(
-            grid_container_style, position, side, auto_repeat_tracks_count,
-            subgrid_span_size);
-      }
-
-      // Handle <integer> explicit position.
-      if (position.IsPositive()) {
-        return position.IntegerPosition() - 1;
-      }
-
-      wtf_size_t resolved_position = abs(position.IntegerPosition()) - 1;
-      wtf_size_t end_of_track =
-          ExplicitGridSizeForSide(grid_container_style, side,
-                                  auto_repeat_tracks_count, subgrid_span_size);
-
-      return end_of_track - resolved_position;
-    }
-    case kNamedGridAreaPosition: {
-      // First attempt to match the grid area's edge to a named grid area: if
-      // there is a named line with the name ''<custom-ident>-start (for
-      // grid-*-start) / <custom-ident>-end'' (for grid-*-end), contributes the
-      // first such line to the grid item's placement.
-      String named_grid_line = position.NamedGridLine();
-      DCHECK(!position.NamedGridLine().IsNull());
-
-      wtf_size_t last_line =
-          ExplicitGridSizeForSide(grid_container_style, side,
-                                  auto_repeat_tracks_count, subgrid_span_size);
-      NamedLineCollection implicit_lines(
-          grid_container_style,
-          ImplicitNamedGridLineForSide(named_grid_line, side),
-          DirectionFromSide(side), last_line, auto_repeat_tracks_count);
-      if (implicit_lines.HasNamedLines()) {
-        return implicit_lines.FirstPosition();
-      }
-
-      // Otherwise, if there is a named line with the specified name,
-      // contributes the first such line to the grid item's placement.
-      NamedLineCollection explicit_lines(
-          grid_container_style, named_grid_line, DirectionFromSide(side),
-          last_line, auto_repeat_tracks_count, is_subgridded_to_parent);
-      if (explicit_lines.HasNamedLines()) {
-        return explicit_lines.FirstPosition();
-      }
-
-      // If none of the above works specs mandate to assume that all the lines
-      // in the implicit grid have this name.
-      return last_line + 1;
-    }
-    case kAutoPosition:
-    case kSpanPosition:
-      // 'auto' and span depend on the opposite position for resolution (e.g.
-      // grid-row: auto / 1 or grid-column: span 3 / "myHeader").
-      NOTREACHED();
-      return 0;
-  }
-  NOTREACHED();
-  return 0;
-}
-
-GridSpan GridPositionsResolver::ResolveGridPositionsFromStyle(
-    const ComputedStyle& grid_container_style,
-    const ComputedStyle& grid_item_style,
-    GridTrackSizingDirection track_direction,
-    wtf_size_t auto_repeat_tracks_count,
-    bool is_subgridded_to_parent,
-    wtf_size_t subgrid_span_size) {
-  GridPosition initial_position, final_position;
-  InitialAndFinalPositionsFromStyle(grid_item_style, track_direction,
-                                    initial_position, final_position);
-
-  const bool initial_should_be_resolved_against_opposite_position =
-      initial_position.ShouldBeResolvedAgainstOppositePosition();
-  const bool final_should_be_resolved_against_opposite_position =
-      final_position.ShouldBeResolvedAgainstOppositePosition();
-
-  if (initial_should_be_resolved_against_opposite_position &&
-      final_should_be_resolved_against_opposite_position) {
-    // We can't get our grid positions without running the auto placement
-    // algorithm.
-    return GridSpan::IndefiniteGridSpan(
-        SpanSizeFromPositions(initial_position, final_position));
-  }
-
-  const GridPositionSide initial_side =
-      (track_direction == kForColumns) ? kColumnStartSide : kRowStartSide;
-  const GridPositionSide final_side =
-      (track_direction == kForColumns) ? kColumnEndSide : kRowEndSide;
-
-  if (initial_should_be_resolved_against_opposite_position) {
-    // Infer the position from the final_position position ('auto / 1' or 'span
-    // 2 / 3' case).
-    int end_line = ResolveGridPositionFromStyle(
-        grid_container_style, final_position, final_side,
-        auto_repeat_tracks_count, is_subgridded_to_parent, subgrid_span_size);
-    return ResolveGridPositionAgainstOppositePosition(
-        grid_container_style, end_line, initial_position, initial_side,
-        auto_repeat_tracks_count, subgrid_span_size);
-  }
-
-  if (final_should_be_resolved_against_opposite_position) {
-    // Infer our position from the initial_position position ('1 / auto' or '3 /
-    // span 2' case).
-    int start_line = ResolveGridPositionFromStyle(
-        grid_container_style, initial_position, initial_side,
-        auto_repeat_tracks_count, is_subgridded_to_parent, subgrid_span_size);
-    return ResolveGridPositionAgainstOppositePosition(
-        grid_container_style, start_line, final_position, final_side,
-        auto_repeat_tracks_count, subgrid_span_size);
-  }
-
-  int start_line = ResolveGridPositionFromStyle(
-      grid_container_style, initial_position, initial_side,
-      auto_repeat_tracks_count, is_subgridded_to_parent, subgrid_span_size);
-  int end_line = ResolveGridPositionFromStyle(
-      grid_container_style, final_position, final_side,
-      auto_repeat_tracks_count, is_subgridded_to_parent, subgrid_span_size);
-
-  if (end_line < start_line) {
-    std::swap(end_line, start_line);
-  } else if (end_line == start_line) {
-    end_line = start_line + 1;
-  }
-
-  return GridSpan::UntranslatedDefiniteGridSpan(start_line, end_line);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/style/grid_positions_resolver.h b/third_party/blink/renderer/core/style/grid_positions_resolver.h
deleted file mode 100644
index 3774d87..0000000
--- a/third_party/blink/renderer/core/style/grid_positions_resolver.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2014 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_GRID_POSITIONS_RESOLVER_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_GRID_POSITIONS_RESOLVER_H_
-
-#include "third_party/blink/renderer/core/style/grid_enums.h"
-#include "third_party/blink/renderer/core/style/grid_position.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-struct GridSpan;
-class LayoutBox;
-class ComputedStyle;
-
-class NamedLineCollection {
- public:
-  NamedLineCollection(const ComputedStyle&,
-                      const String& named_line,
-                      GridTrackSizingDirection,
-                      wtf_size_t last_line,
-                      wtf_size_t auto_repeat_tracks_count,
-                      bool is_subgridded_to_parent = false);
-
-  bool HasNamedLines();
-  wtf_size_t FirstPosition();
-
-  bool Contains(wtf_size_t line);
-
- private:
-  bool HasExplicitNamedLines();
-  wtf_size_t FirstExplicitPosition();
-  const Vector<wtf_size_t>* named_lines_indexes_ = nullptr;
-  const Vector<wtf_size_t>* auto_repeat_named_lines_indexes_ = nullptr;
-  const Vector<wtf_size_t>* implicit_named_lines_indexes_ = nullptr;
-
-  bool is_standalone_grid_;
-  wtf_size_t insertion_point_;
-  wtf_size_t last_line_;
-  wtf_size_t auto_repeat_total_tracks_;
-  wtf_size_t auto_repeat_track_list_length_;
-
-  NamedLineCollection(const NamedLineCollection&) = delete;
-  NamedLineCollection& operator=(const NamedLineCollection&) = delete;
-};
-
-// This is a utility class with all the code related to grid items positions
-// resolution.
-class GridPositionsResolver {
-  DISALLOW_NEW();
-
- public:
-  static wtf_size_t ExplicitGridColumnCount(
-      const ComputedStyle&,
-      wtf_size_t auto_repeat_columns_count,
-      wtf_size_t subgrid_span_size = kNotFound);
-
-  static wtf_size_t ExplicitGridRowCount(
-      const ComputedStyle&,
-      wtf_size_t auto_repeat_rows_count,
-      wtf_size_t subgrid_span_size = kNotFound);
-
-  static wtf_size_t SpanSizeForAutoPlacedItem(const ComputedStyle&,
-                                              GridTrackSizingDirection);
-
-  static GridSpan ResolveGridPositionsFromStyle(
-      const ComputedStyle&,
-      const ComputedStyle&,
-      GridTrackSizingDirection,
-      wtf_size_t auto_repeat_tracks_count,
-      bool is_subgridded_to_parent = false,
-      wtf_size_t subgrid_span_size = kNotFound);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_GRID_POSITIONS_RESOLVER_H_
diff --git a/third_party/blink/renderer/core/style/grid_track_list.cc b/third_party/blink/renderer/core/style/grid_track_list.cc
index 38892bc6..047cce6f 100644
--- a/third_party/blink/renderer/core/style/grid_track_list.cc
+++ b/third_party/blink/renderer/core/style/grid_track_list.cc
@@ -10,10 +10,12 @@
 NGGridTrackRepeater::NGGridTrackRepeater(wtf_size_t repeat_index,
                                          wtf_size_t repeat_size,
                                          wtf_size_t repeat_count,
+                                         wtf_size_t line_name_indices_count,
                                          RepeatType repeat_type)
     : repeat_index(repeat_index),
       repeat_size(repeat_size),
       repeat_count(repeat_count),
+      line_name_indices_count(line_name_indices_count),
       repeat_type(repeat_type) {}
 
 String NGGridTrackRepeater::ToString() const {
@@ -22,6 +24,8 @@
   builder.AppendNumber<wtf_size_t>(repeat_index);
   builder.Append("], [RepeatSize: ");
   builder.AppendNumber<wtf_size_t>(repeat_size);
+  builder.Append("], [LineNameIndicesCount: ");
+  builder.AppendNumber<wtf_size_t>(line_name_indices_count);
   builder.Append("], [RepeatCount: ");
   switch (repeat_type) {
     case RepeatType::kNoRepeat:
@@ -45,8 +49,8 @@
          repeat_count == other.repeat_count && repeat_type == other.repeat_type;
 }
 
-wtf_size_t NGGridTrackList::RepeatCount(const wtf_size_t index,
-                                        const wtf_size_t auto_value) const {
+wtf_size_t NGGridTrackList::RepeatCount(wtf_size_t index,
+                                        wtf_size_t auto_value) const {
   DCHECK_LT(index, RepeaterCount());
   if (index == auto_repeater_index_) {
     return auto_value;
@@ -54,27 +58,31 @@
   return repeaters_[index].repeat_count;
 }
 
-wtf_size_t NGGridTrackList::RepeatIndex(const wtf_size_t index) const {
+wtf_size_t NGGridTrackList::RepeatIndex(wtf_size_t index) const {
   // `repeat_index` is used for sizes, which subgrids don't have.
   DCHECK(!IsSubgriddedAxis());
   DCHECK_LT(index, RepeaterCount());
   return repeaters_[index].repeat_index;
 }
 
-wtf_size_t NGGridTrackList::RepeatSize(const wtf_size_t index) const {
+wtf_size_t NGGridTrackList::RepeatSize(wtf_size_t index) const {
   DCHECK_LT(index, RepeaterCount());
   return repeaters_[index].repeat_size;
 }
 
+wtf_size_t NGGridTrackList::LineNameIndicesCount(wtf_size_t index) const {
+  DCHECK_LT(index, RepeaterCount());
+  return repeaters_[index].line_name_indices_count;
+}
+
 NGGridTrackRepeater::RepeatType NGGridTrackList::RepeatType(
-    const wtf_size_t index) const {
+    wtf_size_t index) const {
   DCHECK_LT(index, RepeaterCount());
   return repeaters_[index].repeat_type;
 }
 
-const GridTrackSize& NGGridTrackList::RepeatTrackSize(
-    const wtf_size_t index,
-    const wtf_size_t n) const {
+const GridTrackSize& NGGridTrackList::RepeatTrackSize(wtf_size_t index,
+                                                      wtf_size_t n) const {
   // Subgrids don't have track sizes associated with them.
   DCHECK(!IsSubgriddedAxis());
   DCHECK_LT(index, RepeaterCount());
@@ -111,7 +119,8 @@
     const Vector<GridTrackSize, 1>& repeater_track_sizes,
     NGGridTrackRepeater::RepeatType repeat_type,
     wtf_size_t repeat_count,
-    wtf_size_t repeat_number_of_lines) {
+    wtf_size_t repeat_number_of_lines,
+    wtf_size_t line_name_indices_count) {
   // Non-subgrid repeaters always have sizes associated with them, while
   // subgrids repeaters never do, as sizes will come from the parent grid.
   DCHECK(!IsSubgriddedAxis() || repeater_track_sizes.empty());
@@ -136,7 +145,13 @@
       if (repeat_size > AvailableTrackCount() / repeat_count) {
         return false;
       }
-      track_count_without_auto_repeat_ += repeat_size * repeat_count;
+      // Don't increment `track_count_without_auto_repeat_` for subgridded
+      // axis. This is used to determine how many tracks are defined for
+      // placement, but this doesn't apply for subgrid, as it is based entirely
+      // on the subgrid span size, which should be used instead.
+      if (!IsSubgriddedAxis()) {
+        track_count_without_auto_repeat_ += repeat_size * repeat_count;
+      }
       break;
     case NGGridTrackRepeater::RepeatType::kAutoFill:
     case NGGridTrackRepeater::RepeatType::kAutoFit:  // Intentional Fallthrough.
@@ -149,7 +164,7 @@
   }
 
   repeaters_.emplace_back(repeater_track_sizes_.size(), repeat_size,
-                          repeat_count, repeat_type);
+                          repeat_count, line_name_indices_count, repeat_type);
   if (!IsSubgriddedAxis()) {
     repeater_track_sizes_.AppendVector(repeater_track_sizes);
   }
diff --git a/third_party/blink/renderer/core/style/grid_track_list.h b/third_party/blink/renderer/core/style/grid_track_list.h
index fdd3715..fb7417b 100644
--- a/third_party/blink/renderer/core/style/grid_track_list.h
+++ b/third_party/blink/renderer/core/style/grid_track_list.h
@@ -27,11 +27,12 @@
   NGGridTrackRepeater(wtf_size_t repeat_index,
                       wtf_size_t repeat_size,
                       wtf_size_t repeat_count,
+                      wtf_size_t line_name_indices_count,
                       RepeatType repeat_type);
   String ToString() const;
   bool operator==(const NGGridTrackRepeater& o) const;
 
-  // |NGGridTrackList| will store the sizes for each track in this repeater
+  // `NGGridTrackList` will store the sizes for each track in this repeater
   // consecutively in a single vector for all repeaters; this index specifies
   // the position of the first track size that belongs to this repeater.
   wtf_size_t repeat_index;
@@ -39,6 +40,13 @@
   wtf_size_t repeat_size;
   // Amount of times the group of tracks are repeated.
   wtf_size_t repeat_count;
+  // Count of line name indices defined in this repeater. This is different than
+  // the count of line names. for instance, a definition of
+  // `repeat(auto-fit, [a b], [c d])` would have a line name count of 4, but the
+  // line name indices count would be 2 (0 and 1). This is necessary for round
+  // tripping repeaters, as we need to know how many indices have line names.
+  // TODO(kschmi): Merge this with `repeat_size`.
+  wtf_size_t line_name_indices_count;
   // Type of repetition.
   RepeatType repeat_type;
 };
@@ -48,19 +56,19 @@
   NGGridTrackList() = default;
   NGGridTrackList(const NGGridTrackList& other) = default;
 
-  // Returns the repeat count of the repeater at |index|, or |auto_value|
+  // Returns the repeat count of the repeater at `index`, or `auto_value`
   // if the repeater is auto.
-  wtf_size_t RepeatCount(const wtf_size_t index,
-                         const wtf_size_t auto_value) const;
-  // Returns the position of the first track size in the repeater at |index|.
-  wtf_size_t RepeatIndex(const wtf_size_t index) const;
-  // Returns the number of tracks in the repeater at |index|.
-  wtf_size_t RepeatSize(const wtf_size_t index) const;
-  // Returns the repeat type of the repeater at |index|.
-  NGGridTrackRepeater::RepeatType RepeatType(const wtf_size_t index) const;
-  // Returns the size of the |n|-th specified track of the repeater at |index|.
-  const GridTrackSize& RepeatTrackSize(const wtf_size_t index,
-                                       const wtf_size_t n) const;
+  wtf_size_t RepeatCount(wtf_size_t index, wtf_size_t auto_value) const;
+  // Returns the position of the first track size in the repeater at `index`.
+  wtf_size_t RepeatIndex(wtf_size_t index) const;
+  // Returns the number of tracks in the repeater at `index`.
+  wtf_size_t RepeatSize(wtf_size_t index) const;
+  // Returns the number line name indices defined at a given repeater `index`.
+  wtf_size_t LineNameIndicesCount(wtf_size_t index) const;
+  // Returns the repeat type of the repeater at `index`.
+  NGGridTrackRepeater::RepeatType RepeatType(wtf_size_t index) const;
+  // Returns the size of the `n`-th specified track of the repeater at `index`.
+  const GridTrackSize& RepeatTrackSize(wtf_size_t index, wtf_size_t n) const;
 
   // Returns the count of repeaters.
   wtf_size_t RepeaterCount() const;
@@ -79,7 +87,8 @@
                    NGGridTrackRepeater::RepeatType repeat_type =
                        NGGridTrackRepeater::RepeatType::kNoRepeat,
                    wtf_size_t repeat_count = 1u,
-                   wtf_size_t repeat_number_of_lines = 1u);
+                   wtf_size_t repeat_number_of_lines = 1u,
+                   wtf_size_t line_name_indices_count = 0u);
   // Returns true if this list contains an auto repeater.
   bool HasAutoRepeater() const;
   // Returns true if this is a subgridded track list.
@@ -106,7 +115,7 @@
   // the same repeater group are stored consecutively.
   Vector<GridTrackSize, 1> repeater_track_sizes_;
 
-  // The index of the automatic repeater, if there is one; |kInvalidRangeIndex|
+  // The index of the automatic repeater, if there is one; `kInvalidRangeIndex`
   // otherwise.
   wtf_size_t auto_repeater_index_{kNotFound};
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index f968f8c4..d851783 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -4051,6 +4051,32 @@
   }
 }
 
+void AXObjectCacheImpl::GetImagesToAnnotate(
+    ui::AXTreeUpdate& update,
+    std::vector<ui::AXNodeData*>& nodes) {
+  for (auto& node : update.nodes) {
+    AXObject* src = ObjectFromAXID(node.id);
+    if (!src || src->IsDetached() || !src->AccessibilityIsIncludedInTree() ||
+        (src->AccessibilityIsIgnored() &&
+         !node.HasState(ax::mojom::blink::State::kFocusable))) {
+      continue;
+    }
+
+    if (src->IsImage()) {
+      nodes.push_back(&node);
+      // This else clause matches links/documents because we would like to find
+      // an image that is in the near-descendant subtree of the link/document,
+      // since that image may be semantically representative of that
+      // link/document. See FindExactlyOneInnerImageInMaxDepthThree (not in
+      // this file), which is used by the caller of this method to find such
+      // an image.
+    } else if ((src->IsLink() || ui::IsPlatformDocument(node.role)) &&
+               node.GetNameFrom() != ax::mojom::blink::NameFrom::kAttribute) {
+      nodes.push_back(&node);
+    }
+  }
+}
+
 bool AXObjectCacheImpl::AddPendingEvent(const ui::AXEvent& event,
                                         bool insert_at_beginning) {
   if (insert_at_beginning)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index 281d720..7363c55 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -477,6 +477,9 @@
       bool& had_load_complete_messages,
       bool& need_to_send_location_changes) override;
 
+  void GetImagesToAnnotate(ui::AXTreeUpdate& updates,
+                           std::vector<ui::AXNodeData*>& nodes) override;
+
   void ClearDirtyObjectsAndPendingEvents() override {
     dirty_objects_.clear();
     pending_events_.clear();
diff --git a/third_party/blink/renderer/modules/exported/web_ax_context.cc b/third_party/blink/renderer/modules/exported/web_ax_context.cc
index 2f71bef..008335c 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_context.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_context.cc
@@ -110,6 +110,11 @@
       had_load_complete_messages, need_to_send_location_changes);
 }
 
+void WebAXContext::GetImagesToAnnotate(ui::AXTreeUpdate& updates,
+                                       std::vector<ui::AXNodeData*>& nodes) {
+  private_->GetAXObjectCache().GetImagesToAnnotate(updates, nodes);
+}
+
 void WebAXContext::ClearDirtyObjectsAndPendingEvents() {
   if (!HasActiveDocument()) {
     return;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index ca0e5f9..4f6d0e4c 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -161,6 +161,91 @@
       .NearestScrollTranslationNode();
 }
 
+bool PaintArtifactCompositor::NeedsCompositedScrolling(
+    const TransformPaintPropertyNode& scroll_translation) const {
+  DCHECK(scroll_translation.ScrollNode());
+  if (scroll_translation.HasDirectCompositingReasons()) {
+    return true;
+  }
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    auto it = scroll_translation_nodes_.find(&scroll_translation);
+    if (it != scroll_translation_nodes_.end()) {
+      return it->value;
+    }
+  }
+  return false;
+}
+
+bool PaintArtifactCompositor::ComputeNeedsCompositedScrolling(
+    const PaintArtifact& artifact,
+    Vector<PaintChunk>::const_iterator chunk_cursor) const {
+  // The chunk must be a ScrollHitTest chunk which contains no display items.
+  DCHECK(chunk_cursor->hit_test_data);
+  DCHECK(chunk_cursor->hit_test_data->scroll_translation);
+  DCHECK_EQ(chunk_cursor->size(), 0u);
+  const auto& scroll_translation =
+      *chunk_cursor->hit_test_data->scroll_translation;
+  DCHECK(scroll_translation.ScrollNode());
+  // This function should be called before scroll_translation is inserted into
+  // scroll_translation_nodes_.
+  DCHECK(!scroll_translation_nodes_.Contains(&scroll_translation));
+
+  if (!RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+    return scroll_translation.HasDirectCompositingReasons();
+  }
+
+  if (scroll_translation.HasDirectCompositingReasons()) {
+    return true;
+  }
+  if (RuntimeEnabledFeatures::PreferNonCompositedScrollingEnabled()) {
+    return false;
+  }
+  // Don't automatically composite non-user-scrollable scrollers.
+  if (!scroll_translation.ScrollNode()->UserScrollableHorizontal() &&
+      !scroll_translation.ScrollNode()->UserScrollableVertical()) {
+    return false;
+  }
+  if (lcd_text_preference_ != LCDTextPreference::kStronglyPreferred ||
+      chunk_cursor + 1 == artifact.PaintChunks().end()) {
+    return true;
+  }
+  // Normally the next chunk contains the scrolling background which normally
+  // defines the opaqueness of the scrolling contents. If it has an opaque rect
+  // covering the whole scrolling contents, we can use composited scrolling
+  // without losing LCD text.
+  const PaintChunk& next_chunk = *(chunk_cursor + 1);
+  return &next_chunk.properties.Transform().Unalias() == &scroll_translation &&
+         &next_chunk.properties.Clip().Unalias() ==
+             scroll_translation.ScrollNode()->OverflowClipNode() &&
+         &next_chunk.properties.Effect().Unalias() ==
+             &chunk_cursor->properties.Effect().Unalias() &&
+         next_chunk.rect_known_to_be_opaque.Contains(
+             scroll_translation.ScrollNode()->ContentsRect());
+}
+
+PendingLayer::CompositingType PaintArtifactCompositor::ChunkCompositingType(
+    const PaintArtifact& artifact,
+    const PaintChunk& chunk) const {
+  if (chunk.hit_test_data && chunk.hit_test_data->scroll_translation &&
+      NeedsCompositedScrolling(*chunk.hit_test_data->scroll_translation)) {
+    return PendingLayer::kScrollHitTestLayer;
+  }
+  if (chunk.size() == 1) {
+    const auto& item = artifact.GetDisplayItemList()[chunk.begin_index];
+    if (item.IsForeignLayer()) {
+      return PendingLayer::kForeignLayer;
+    }
+    if (const auto* scrollbar = DynamicTo<ScrollbarDisplayItem>(item)) {
+      if (const auto* scroll_translation = scrollbar->ScrollTranslation()) {
+        if (NeedsCompositedScrolling(*scroll_translation)) {
+          return PendingLayer::kScrollbarLayer;
+        }
+      }
+    }
+  }
+  return PendingLayer::kOther;
+}
+
 namespace {
 
 cc::Layer* ForeignLayer(const PaintChunk& chunk,
@@ -420,28 +505,34 @@
   // O(p) due to copying the chunk list. Subtotal: O((qd + p)d) = O(qd^2 + pd)
   // Assuming p > d, the total complexity would be O(pqd + qd^2 + pd) = O(pqd)
   while (chunk_cursor != artifact->PaintChunks().end()) {
-    // Track painted ScrollTranslation nodes. with ScrollUnification enabled,
-    // PaintArtifactCompositor::Update uses this to know which
-    // ScrollTranslation nodes we need to create composited Transform nodes
-    // for.
-    if (chunk_cursor->hit_test_data &&
-        chunk_cursor->hit_test_data->scroll_translation) {
-      scroll_translation_nodes_.insert(
-          chunk_cursor->hit_test_data->scroll_translation.get());
-    }
     // Look at the effect node of the next chunk. There are 3 possible cases:
     // A. The next chunk belongs to the current group but no subgroup.
     // B. The next chunk does not belong to the current group.
     // C. The next chunk belongs to some subgroup of the current group.
     const auto& chunk_effect = chunk_cursor->properties.Effect().Unalias();
     if (&chunk_effect == &current_group) {
+      // Track painted ScrollTranslation nodes and their composited scrolling
+      // status. With ScrollUnification enabled, Update() also uses this to
+      // know which ScrollTranslation nodes we need to create composited
+      // Transform nodes for.
+      if (chunk_cursor->hit_test_data &&
+          chunk_cursor->hit_test_data->scroll_translation) {
+        scroll_translation_nodes_.insert(
+            chunk_cursor->hit_test_data->scroll_translation.get(),
+            ComputeNeedsCompositedScrolling(*artifact, chunk_cursor));
+      }
       pending_layers_.emplace_back(artifact, *chunk_cursor);
+      pending_layers_.back().SetCompositingType(
+          ChunkCompositingType(*artifact, *chunk_cursor));
       ++chunk_cursor;
       // force_draws_content doesn't apply to pending layers that require own
       // layer, specifically scrollbar layers, foreign layers, scroll hit
       // testing layers.
-      if (pending_layers_.back().ChunkRequiresOwnLayer())
+      if (pending_layers_.back().ChunkRequiresOwnLayer()) {
+        // TODO(crbug.com/1414885): conditionally composite ScrollHitTest and
+        // scrollbar in CompositeScrollAfterPaint.
         continue;
+      }
     } else {
       const EffectPaintPropertyNode* subgroup =
           StrictUnaliasedChildOfAlongPath(current_group, chunk_effect);
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
index d75ddd1e..78d23a3 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -307,6 +307,14 @@
       CompositorElementId& mask_isolation_id,
       CompositorElementId& mask_effect_id) final;
 
+  bool NeedsCompositedScrolling(
+      const TransformPaintPropertyNode& scroll_translation) const final;
+  bool ComputeNeedsCompositedScrolling(
+      const PaintArtifact&,
+      Vector<PaintChunk>::const_iterator chunk_cursor) const;
+  PendingLayer::CompositingType ChunkCompositingType(const PaintArtifact&,
+                                                     const PaintChunk&) const;
+
   static void UpdateRenderSurfaceForEffects(
       cc::EffectTree&,
       const cc::LayerList&,
@@ -341,9 +349,10 @@
   class OldPendingLayerMatcher;
   PendingLayers pending_layers_;
 
-  // ScrollTranslationNodes of the PaintArtifact which are painted.
+  // ScrollTranslationNodes of the PaintArtifact that are painted.
   // This member variable is only used in PaintArtifactCompositor::Update.
-  HashSet<const TransformPaintPropertyNode*> scroll_translation_nodes_;
+  // The value indicates if the scroll should be composited.
+  HashMap<const TransformPaintPropertyNode*, bool> scroll_translation_nodes_;
 
   friend class StubChromeClientForCAP;
   friend class PaintArtifactCompositorTest;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
index 25cd700..98435ac6 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
@@ -23,23 +23,6 @@
 
 namespace {
 
-bool IsCompositedScrollHitTest(const PaintChunk& chunk) {
-  if (!chunk.hit_test_data)
-    return false;
-  const auto scroll_translation = chunk.hit_test_data->scroll_translation;
-  return scroll_translation &&
-         scroll_translation->HasDirectCompositingReasons();
-}
-
-bool IsCompositedScrollbar(const DisplayItem& item) {
-  if (const auto* scrollbar = DynamicTo<ScrollbarDisplayItem>(item)) {
-    const auto* scroll_translation = scrollbar->ScrollTranslation();
-    return scroll_translation &&
-           scroll_translation->HasDirectCompositingReasons();
-  }
-  return false;
-}
-
 // Snap |bounds| if within floating-point numeric limits of an integral rect.
 void PreserveNearIntegralBounds(gfx::RectF& bounds) {
   constexpr float kTolerance = 1e-5f;
@@ -64,8 +47,7 @@
       is_solid_color_(first_chunk.background_color.is_solid_color),
       chunks_(std::move(artifact), first_chunk),
       property_tree_state_(
-          first_chunk.properties.GetPropertyTreeState().Unalias()),
-      compositing_type_(kOther) {
+          first_chunk.properties.GetPropertyTreeState().Unalias()) {
   DCHECK(!ChunkRequiresOwnLayer() || first_chunk.size() <= 1u);
   // Though text_known_to_be_on_opaque_background is only meaningful when
   // has_text is true, we expect text_known_to_be_on_opaque_background to be
@@ -79,17 +61,6 @@
     }
   }
   rect_known_to_be_opaque_.Intersect(bounds_);
-
-  if (IsCompositedScrollHitTest(first_chunk)) {
-    compositing_type_ = kScrollHitTestLayer;
-  } else if (first_chunk.size()) {
-    const auto& first_display_item = FirstDisplayItem();
-    if (first_display_item.IsForeignLayer()) {
-      compositing_type_ = kForeignLayer;
-    } else if (IsCompositedScrollbar(first_display_item)) {
-      compositing_type_ = kScrollbarLayer;
-    }
-  }
 }
 
 gfx::Vector2dF PendingLayer::LayerOffset() const {
@@ -138,14 +109,18 @@
 
 std::unique_ptr<JSONObject> PendingLayer::ToJSON() const {
   std::unique_ptr<JSONObject> result = std::make_unique<JSONObject>();
+  result->SetString("debug_name", DebugName());
   result->SetArray("bounds", RectAsJSONArray(bounds_));
   result->SetArray("rect_known_to_be_opaque",
                    RectAsJSONArray(rect_known_to_be_opaque_));
-  result->SetObject("property_tree_state", GetPropertyTreeState().ToJSON());
+  result->SetBoolean("text_known_to_be_on_opaque_background",
+                     text_known_to_be_on_opaque_background_);
+  result->SetString("property_tree_state", GetPropertyTreeState().ToString());
   result->SetArray("offset_of_decomposited_transforms",
                    VectorAsJSONArray(offset_of_decomposited_transforms_));
   result->SetArray("paint_chunks", chunks_.ToJSON());
   result->SetBoolean("draws_content", DrawsContent());
+  result->SetBoolean("is_solid_color", is_solid_color_);
   return result;
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h
index 302daef..ba4a6c67 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h
@@ -198,7 +198,7 @@
   gfx::Vector2dF offset_of_decomposited_transforms_;
   PaintPropertyChangeType change_of_decomposited_transforms_ =
       PaintPropertyChangeType::kUnchanged;
-  CompositingType compositing_type_;
+  CompositingType compositing_type_ = kOther;
 
   // This is set to non-null after layerization if ChunkRequiresOwnLayer() or
   // UsesSolidColorLayer() is true.
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 669b600..cd0dc61 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -612,7 +612,18 @@
   compositor_node.transform_id =
       scroll_translation_node.CcNodeId(new_sequence_number_);
   compositor_node.is_composited =
-      scroll_translation_node.HasDirectCompositingReasons();
+      client_.NeedsCompositedScrolling(scroll_translation_node);
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled() &&
+      !RuntimeEnabledFeatures::PreferNonCompositedScrollingEnabled() &&
+      !compositor_node.is_composited &&
+      compositor_node.main_thread_scrolling_reasons ==
+          cc::MainThreadScrollingReason::kNotScrollingOnMain) {
+    // TODO(crbug.com/1414885): We can't distinguish kNotOpaqueForTextAndLCDText
+    // and kCantPaintScrollingBackgroundAndLCDText here. We should probably
+    // merge the two reasons for CompositeScrollAfterPaint.
+    compositor_node.main_thread_scrolling_reasons =
+        cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText;
+  }
 
   scroll_node.SetCcNodeId(new_sequence_number_, id);
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
index 596c398..137df80 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
@@ -46,6 +46,8 @@
       bool needs_layer,
       CompositorElementId& mask_isolation_id,
       CompositorElementId& mask_effect_id) = 0;
+  virtual bool NeedsCompositedScrolling(
+      const TransformPaintPropertyNode& scroll_translation) const = 0;
 };
 
 // Mutates a cc property tree to reflect Blink paint property tree
diff --git a/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc b/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
index b5c4fcf..adeb5d4 100644
--- a/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
@@ -92,7 +92,7 @@
   if (disable_expansion) {
     return false;
   }
-  if (!RuntimeEnabledFeatures::UnifiedScrollPaintingEnabled() &&
+  if (!RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled() &&
       // Don't expand for non-composited scrolling.
       !scroll_translation.HasDirectCompositingReasons()) {
     return false;
@@ -363,7 +363,7 @@
     const gfx::Vector2dF& delta,
     const TransformPaintPropertyNode& scroll_translation) {
   if (!scroll_translation.ScrollNode() ||
-      (!RuntimeEnabledFeatures::UnifiedScrollPaintingEnabled() &&
+      (!RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled() &&
        !scroll_translation.HasDirectCompositingReasons())) {
     return !delta.IsZero();
   }
diff --git a/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc b/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
index 3821822..61fb0a4c 100644
--- a/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
@@ -15,9 +15,9 @@
 
 class CullRectTest : public testing::Test,
                      public testing::WithParamInterface<bool>,
-                     private ScopedUnifiedScrollPaintingForTest {
+                     private ScopedCompositeScrollAfterPaintForTest {
  protected:
-  CullRectTest() : ScopedUnifiedScrollPaintingForTest(GetParam()) {}
+  CullRectTest() : ScopedCompositeScrollAfterPaintForTest(GetParam()) {}
 
   bool ApplyPaintProperties(
       CullRect& cull_rect,
@@ -139,7 +139,7 @@
   auto& scroll_translation = state.Transform();
 
   CullRect cull_rect(gfx::Rect(0, 0, 50, 100));
-  if (RuntimeEnabledFeatures::UnifiedScrollPaintingEnabled()) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
     // Same as ApplyScrollTranslationPartialScrollingContents.
     EXPECT_TRUE(ApplyScrollTranslation(cull_rect, scroll_translation));
     EXPECT_EQ(gfx::Rect(20, 1010, 7030, 7000), cull_rect.Rect());
@@ -234,7 +234,7 @@
   auto& scroll_translation = state.Transform();
 
   CullRect cull_rect(gfx::Rect(0, 0, 50, 100));
-  if (RuntimeEnabledFeatures::UnifiedScrollPaintingEnabled()) {
+  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
     // Same as ApplyScrollTranslationWholeScrollingContents.
     EXPECT_TRUE(ApplyScrollTranslation(cull_rect, scroll_translation));
     EXPECT_EQ(gfx::Rect(20, 10, 2000, 2000), cull_rect.Rect());
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
index b954b21..136fe4f 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
@@ -237,10 +237,13 @@
 
   std::unique_ptr<JSONObject> ToJSONBase() const {
     auto json = std::make_unique<JSONObject>();
-    if (Parent())
+    json->SetString("this", String::Format("%p", this));
+    if (Parent()) {
       json->SetString("parent", String::Format("%p", Parent()));
-    if (IsParentAlias())
+    }
+    if (IsParentAlias()) {
       json->SetBoolean("is_alias", true);
+    }
     if (NodeChanged() != PaintPropertyChangeType::kUnchanged) {
       json->SetString("changed",
                       PaintPropertyChangeTypeToString(NodeChanged()));
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 9f0ae46..c42d017 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -627,6 +627,9 @@
       base_feature: "none",
     },
     {
+      name: "CompositeScrollAfterPaint",
+    },
+    {
       name: "ComputedAccessibilityInfo",
       status: "experimental",
       base_feature: "none",
@@ -3397,9 +3400,6 @@
       name: "UnexposedTaskIds",
       base_feature: "none",
     },
-    {
-      name: "UnifiedScrollPainting",
-    },
     // This is a reverse OT used for a phased deprecation, on desktop
     // https://crbug.com/1071424
     {
diff --git a/third_party/blink/renderer/platform/testing/paint_test_configurations.h b/third_party/blink/renderer/platform/testing/paint_test_configurations.h
index 8ebef51..bf066c7 100644
--- a/third_party/blink/renderer/platform/testing/paint_test_configurations.h
+++ b/third_party/blink/renderer/platform/testing/paint_test_configurations.h
@@ -19,21 +19,21 @@
   kUnderInvalidationChecking = 1 << 0,
   kScrollUnification = 1 << 1,
   kSolidColorLayers = 1 << 2,
-  kUnifiedScrollPainting = 1 << 3,
+  kCompositeScrollAfterPaint = 1 << 3,
 };
 
 class PaintTestConfigurations
     : public testing::WithParamInterface<unsigned>,
       private ScopedPaintUnderInvalidationCheckingForTest,
       private ScopedSolidColorLayersForTest,
-      private ScopedUnifiedScrollPaintingForTest {
+      private ScopedCompositeScrollAfterPaintForTest {
  public:
   PaintTestConfigurations()
       : ScopedPaintUnderInvalidationCheckingForTest(GetParam() &
                                                     kUnderInvalidationChecking),
         ScopedSolidColorLayersForTest(GetParam() & kSolidColorLayers),
-        ScopedUnifiedScrollPaintingForTest(GetParam() &
-                                           kUnifiedScrollPainting) {
+        ScopedCompositeScrollAfterPaintForTest(GetParam() &
+                                               kCompositeScrollAfterPaint) {
     if (GetParam() & kScrollUnification) {
       feature_list_.InitAndEnableFeature(::features::kScrollUnification);
     } else {
@@ -55,13 +55,17 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-// For now this has only one configuration, but can be extended in the future
-// to include more configurations.
+// Note: If a new test fails with kCompositeScrollAfterPaint, please add the
+// following at the beginning of the test to skip it temporarily:
+//  if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
+//    // TODO(crbug.com/1414885): Fix this test.
+//    return;
+//  }
 #define INSTANTIATE_PAINT_TEST_SUITE_P(test_class)                \
   INSTANTIATE_TEST_SUITE_P(                                       \
       All, test_class,                                            \
       ::testing::Values(0, kScrollUnification, kSolidColorLayers, \
-                        kUnifiedScrollPainting))
+                        kScrollUnification | kCompositeScrollAfterPaint))
 
 }  // namespace blink
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 9dd7feb..8c81caf 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1715,7 +1715,6 @@
 crbug.com/1232504 [ Win ] external/wpt/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html [ Timeout ]
 crbug.com/1234199 [ Linux ] external/wpt/html/semantics/embedded-content/the-object-element/object-events.html [ Crash Failure Pass Timeout ]
 crbug.com/1234199 [ Mac ] external/wpt/html/semantics/embedded-content/the-object-element/object-events.html [ Crash Failure Pass Timeout ]
-crbug.com/1394141 [ Win ] external/wpt/html/semantics/disabled-elements/event-propagate-disabled.tentative.html [ Failure Timeout ]
 crbug.com/1395815 external/wpt/html/semantics/forms/the-input-element/range-tick-marks-02.html [ Failure ]
 crbug.com/1004760 [ Mac ] external/wpt/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html [ Timeout ]
 crbug.com/1191547 external/wpt/html/semantics/forms/the-label-element/proxy-modifier-click-to-associated-element.tentative.html [ Timeout ]
@@ -3510,7 +3509,6 @@
 crbug.com/618969 external/wpt/css/css-grid/subgrid/grid-gap-larger-002.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/grid-gap-normal-001.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/grid-gap-smaller-001.html [ Failure ]
-crbug.com/618969 external/wpt/css/css-grid/subgrid/grid-template-computed-nogrid.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/independent-formatting-context.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/item-percentage-height-001.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/line-names-007.html [ Failure ]
@@ -5025,6 +5023,11 @@
 # Sheriff 2021-06-14
 crbug.com/1219499 [ Release Win ] external/wpt/websockets/Create-blocked-port.any.html?wpt_flags=h2 [ Failure Pass Timeout ]
 
+# Waiting for resolution on disabled fieldsets in https://github.com/whatwg/html/issues/5886
+# We want to make fieldsets not disableable due to crbug.com/1422096 and crbug.com/1422547
+# which breaks this exhaustive test, but it will be updated after whatwg/html resolution.
+crbug.com/588760 external/wpt/html/semantics/disabled-elements/event-propagate-disabled.tentative.html [ Failure ]
+
 # Sheriff 2021-06-15
 crbug.com/1220007 [ Linux ] fullscreen/full-screen-iframe-allowed-video.html [ Failure Pass Timeout ]
 
@@ -5608,7 +5611,6 @@
 # Sheriff 2022-04-05
 crbug.com/1313282 inspector-protocol/input/dispatchMouseEvent.js [ Failure Pass Timeout ]
 crbug.com/1254163 [ Mac10.13 ] ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling.htm [ Failure ]
-crbug.com/1313396 [ Linux ] external/wpt/dom/events/Event-dispatch-on-disabled-elements.html [ Failure Pass ]
 crbug.com/1194945 external/wpt/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html [ Failure Pass ]
 crbug.com/1244896 fast/mediacapturefromelement/CanvasCaptureMediaStream-set-size-too-large.html [ Failure Pass Timeout ]
 
diff --git a/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-default.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-default.tentative.https.sub.html
index a7c5a95..884ad92 100644
--- a/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-default.tentative.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-default.tentative.https.sub.html
@@ -2,6 +2,7 @@
 <body>
   <script src=/resources/testharness.js></script>
   <script src=/resources/testharnessreport.js></script>
+  <script src=/browsing-topics/resources/navigation-header-util.sub.js></script>
   <script src=/browsing-topics/resources/permissions-policy-util.sub.js></script>
   <script>
     'use strict';
@@ -37,5 +38,18 @@
       let topics_header = await response.text();
       assert_equals(topics_header, "");
     }, header + 'allows the \'Sec-Browsing-Topics\' header to be sent for the cross-origin topics fetch request.');
+
+    async_test(t => {
+      test_topics_iframe_navigation_header(
+          t, /*has_browsing_topics_attribute=*/true, /*is_same_origin=*/true,
+          expect_topics_header_available);
+    }, header + ' allows the \'Sec-Browsing-Topics\' header to be sent for the same-origin iframe navigation request.');
+
+    async_test(t => {
+      test_topics_iframe_navigation_header(
+          t, /*has_browsing_topics_attribute=*/true, /*is_same_origin=*/false,
+          expect_topics_header_available);
+    }, header + ' allows the \'Sec-Browsing-Topics\' header to be sent for the cross-origin iframe navigation request.');
+
   </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-none.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-none.tentative.https.sub.html
index 7d33f3b..2f80daa2 100644
--- a/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-none.tentative.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-none.tentative.https.sub.html
@@ -2,6 +2,7 @@
 <body>
   <script src=/resources/testharness.js></script>
   <script src=/resources/testharnessreport.js></script>
+  <script src=/browsing-topics/resources/navigation-header-util.sub.js></script>
   <script src=/browsing-topics/resources/permissions-policy-util.sub.js></script>
   <script>
     'use strict';
@@ -42,5 +43,17 @@
       let topics_header = await response.text();
       assert_equals(topics_header, "NO_TOPICS_HEADER");
     }, header + 'disallows the \'Sec-Browsing-Topics\' header to be sent for the cross-origin topics fetch request.');
+
+    async_test(t => {
+      test_topics_iframe_navigation_header(
+          t, /*has_browsing_topics_attribute=*/true, /*is_same_origin=*/true,
+          expect_topics_header_unavailable);
+    }, header + ' disallows the \'Sec-Browsing-Topics\' header to be sent for the same-origin iframe navigation request.');
+
+    async_test(t => {
+      test_topics_iframe_navigation_header(
+          t, /*has_browsing_topics_attribute=*/true, /*is_same_origin=*/false,
+          expect_topics_header_unavailable);
+    }, header + ' disallows the \'Sec-Browsing-Topics\' header to be sent for the cross-origin iframe navigation request.');
   </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-self.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-self.tentative.https.sub.html
index 7647998..60126679 100644
--- a/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-self.tentative.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/browsing-topics/browsing-topics-permissions-policy-self.tentative.https.sub.html
@@ -2,6 +2,7 @@
 <body>
   <script src=/resources/testharness.js></script>
   <script src=/resources/testharnessreport.js></script>
+  <script src=/browsing-topics/resources/navigation-header-util.sub.js></script>
   <script src=/browsing-topics/resources/permissions-policy-util.sub.js></script>
   <script>
     'use strict';
@@ -49,5 +50,17 @@
       let topics_header = await response.text();
       assert_equals(topics_header, "NO_TOPICS_HEADER");
     }, header + 'disallows the \'Sec-Browsing-Topics\' header to be sent for the redirect of a topics fetch request, where the redirect has a cross-origin URL.');
+
+    async_test(t => {
+      test_topics_iframe_navigation_header(
+          t, /*has_browsing_topics_attribute=*/true, /*is_same_origin=*/true,
+          expect_topics_header_available);
+    }, header + ' allows the \'Sec-Browsing-Topics\' header to be sent for the same-origin iframe navigation request.');
+
+    async_test(t => {
+      test_topics_iframe_navigation_header(
+          t, /*has_browsing_topics_attribute=*/true, /*is_same_origin=*/false,
+          expect_topics_header_unavailable);
+    }, header + ' disallows the \'Sec-Browsing-Topics\' header to be sent for the cross-origin iframe navigation request.');
   </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/browsing-topics/iframe-topics-attribute-insecure-context.tentative.http.sub.html b/third_party/blink/web_tests/external/wpt/browsing-topics/iframe-topics-attribute-insecure-context.tentative.http.sub.html
new file mode 100644
index 0000000..19c79e0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/browsing-topics/iframe-topics-attribute-insecure-context.tentative.http.sub.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/browsing-topics/resources/navigation-header-util.sub.js></script>
+  <script>
+    async_test(t => {
+      test_topics_iframe_navigation_header(
+          t, /*has_browsing_topics_attribute=*/true, /*is_same_origin=*/false,
+          expect_topics_header_unavailable);
+    }, 'test <iframe browsingtopics src=[url]></iframe> in an insecure context, where the browsingtopics attribute is set via IDL.');
+
+    async_test(t => {
+      const same_origin_src = '/browsing-topics/resources/check-topics-request-header-notify-parent.py';
+      const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+            same_origin_src;
+
+      let frame = document.createElement('iframe');
+
+      window.addEventListener('message', t.step_func(function handler(evt) {
+        if (evt.source === frame.contentWindow) {
+          assert_equals(evt.data.topicsHeader, 'NO_TOPICS_HEADER');
+
+          document.body.removeChild(frame);
+          window.removeEventListener('message', handler);
+          t.done();
+        }
+      }));
+
+      document.body.appendChild(frame);
+
+      frame.setAttribute("browsingtopics", "123");
+      frame.src = cross_origin_src;
+    }, 'test <iframe browsingtopics src=[url]></iframe> in an insecure context, where the browsingtopics attribute is set via setAttribute().');
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/browsing-topics/iframe-topics-attribute.tentative.https.html b/third_party/blink/web_tests/external/wpt/browsing-topics/iframe-topics-attribute.tentative.https.html
new file mode 100644
index 0000000..b90f967
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/browsing-topics/iframe-topics-attribute.tentative.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/browsing-topics/resources/navigation-header-util.sub.js></script>
+  <script>
+    async_test(t => {
+      test_topics_iframe_navigation_header(
+          t, /*has_browsing_topics_attribute=*/false, /*is_same_origin=*/false,
+          expect_topics_header_unavailable);
+    }, 'test <iframe src=[url]></iframe>');
+
+    async_test(t => {
+      test_topics_iframe_navigation_header(
+          t, /*has_browsing_topics_attribute=*/true, /*is_same_origin=*/false,
+          expect_topics_header_available);
+    }, 'test <iframe browsingtopics src=[url]></iframe>');
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/browsing-topics/resources/check-topics-request-header-notify-parent.py b/third_party/blink/web_tests/external/wpt/browsing-topics/resources/check-topics-request-header-notify-parent.py
new file mode 100644
index 0000000..98c77c2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/browsing-topics/resources/check-topics-request-header-notify-parent.py
@@ -0,0 +1,18 @@
+def main(request, response):
+    """
+    Returns an HTML response that notifies its parent frame the topics header
+    via postMessage
+    """
+
+    topics_header = request.headers.get(b"sec-browsing-topics", b"NO_TOPICS_HEADER")
+
+    headers = [(b"Content-Type", b"text/html"),
+               (b"Access-Control-Allow-Origin", b"*")]
+    content = b'''
+<script>
+  let parentOrOpener = window.opener || window.parent;
+  parentOrOpener.postMessage({ topicsHeader: '%s'}, "*");
+</script>
+''' % (topics_header)
+
+    return 200, headers, content
diff --git a/third_party/blink/web_tests/external/wpt/browsing-topics/resources/navigation-header-util.sub.js b/third_party/blink/web_tests/external/wpt/browsing-topics/resources/navigation-header-util.sub.js
new file mode 100644
index 0000000..b3bec79
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/browsing-topics/resources/navigation-header-util.sub.js
@@ -0,0 +1,36 @@
+function test_topics_iframe_navigation_header(
+    test, has_browsing_topics_attribute, is_same_origin, expect_topics_header_available_func) {
+  const same_origin_src = '/browsing-topics/resources/check-topics-request-header-notify-parent.py';
+  const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+    same_origin_src;
+
+  let frame = document.createElement('iframe');
+  frame.src = is_same_origin ? same_origin_src : cross_origin_src;
+
+  if (has_browsing_topics_attribute) {
+    frame.browsingTopics = true;
+  }
+
+  window.addEventListener('message', test.step_func(function handler(evt) {
+    if (evt.source === frame.contentWindow) {
+      expect_topics_header_available_func(evt.data);
+
+      document.body.removeChild(frame);
+      window.removeEventListener('message', handler);
+      test.done();
+    }
+  }));
+
+  document.body.appendChild(frame);
+}
+
+function expect_topics_header_unavailable(data) {
+  assert_equals(data.topicsHeader, 'NO_TOPICS_HEADER');
+}
+
+function expect_topics_header_available(data) {
+  // An empty result indicates that the request was eligible for topics.
+  // Currently, the web-platform-tests framework does not support actually
+  // handling the topics request.
+  assert_equals(data.topicsHeader, '');
+}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/borders/discrete-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/CSS2/borders/discrete-no-interpolation.html
new file mode 100644
index 0000000..0268b675
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/borders/discrete-no-interpolation.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/w3c/csswg-drafts/issues/4441">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<body>
+<script>
+test_no_interpolation({
+  property: 'border-left-style',
+  from: 'initial',
+  to: 'dotted'
+});
+
+test_no_interpolation({
+  property: 'border-right-style',
+  from: 'initial',
+  to: 'dotted'
+});
+
+test_no_interpolation({
+  property: 'border-top-style',
+  from: 'initial',
+  to: 'dotted'
+});
+
+test_no_interpolation({
+  property: 'border-bottom-style',
+  from: 'initial',
+  to: 'dotted'
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/CSS2/floats-clear/clear-no-interpolation.html
similarity index 88%
copy from third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
copy to third_party/blink/web_tests/external/wpt/css/CSS2/floats-clear/clear-no-interpolation.html
index a1a48d5..05db269e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/floats-clear/clear-no-interpolation.html
@@ -8,8 +8,8 @@
 <body>
 <script>
 test_no_interpolation({
-  property: 'background-repeat',
+  property: 'clear',
   from: 'initial',
-  to: 'round'
+  to: 'both'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/CSS2/tables/border-collapse-no-interpolation.html
similarity index 88%
rename from third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
rename to third_party/blink/web_tests/external/wpt/css/CSS2/tables/border-collapse-no-interpolation.html
index a1a48d5..800cc68 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/tables/border-collapse-no-interpolation.html
@@ -8,8 +8,8 @@
 <body>
 <script>
 test_no_interpolation({
-  property: 'background-repeat',
+  property: 'border-collapse',
   from: 'initial',
-  to: 'round'
+  to: 'collapse'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/discrete-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/discrete-no-interpolation.html
new file mode 100644
index 0000000..d3d3421
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/discrete-no-interpolation.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/w3c/csswg-drafts/issues/4441">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<body>
+<script>
+test_no_interpolation({
+  property: 'background-attachment',
+  from: 'initial',
+  to: 'fixed'
+});
+
+test_no_interpolation({
+  property: 'background-blend-mode',
+  from: 'initial',
+  to: 'overlay'
+});
+
+test_no_interpolation({
+  property: 'background-clip',
+  from: 'initial',
+  to: 'content-box'
+});
+
+test_no_interpolation({
+  property: 'background-origin',
+  from: 'initial',
+  to: 'border-box'
+});
+
+test_no_interpolation({
+  property: 'background-repeat',
+  from: 'initial',
+  to: 'round'
+});
+
+test_no_interpolation({
+  property: 'border-image-repeat',
+  from: 'initial',
+  to: 'round'
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-break/animation/break-no-interpolation.html
similarity index 63%
copy from third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
copy to third_party/blink/web_tests/external/wpt/css/css-break/animation/break-no-interpolation.html
index a1a48d5..8a7bf7a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/animation/break-no-interpolation.html
@@ -8,8 +8,20 @@
 <body>
 <script>
 test_no_interpolation({
-  property: 'background-repeat',
+  property: 'break-after',
   from: 'initial',
-  to: 'round'
+  to: 'avoid'
+});
+
+test_no_interpolation({
+  property: 'break-before',
+  from: 'initial',
+  to: 'avoid'
+});
+
+test_no_interpolation({
+  property: 'break-inside',
+  from: 'initial',
+  to: 'avoid'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid.html
index 11b0393..b1f80a7 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid.html
@@ -26,6 +26,7 @@
 test_computed_value("grid-template-columns", "1px repeat(auto-fill, 2px) 3px");
 test_computed_value("grid-template-columns", "1px repeat(auto-fit, 2px) 3px");
 test_computed_value("grid-template-columns", "1px [a] repeat(1, 2px 3px) [b] 4px");
+test_computed_value("grid-template-columns", "1px [a] repeat(2, 2px 3px) [b] 4px");
 test_computed_value("grid-template-columns", "1px [a] repeat(auto-fill, 2px 3px) [b] 4px");
 test_computed_value("grid-template-columns", "1px [a] repeat(auto-fit, 2px 3px) [b] 4px");
 test_computed_value("grid-template-columns", "1px [a] repeat(1, [b] 2px [c]) [d] 3px");
@@ -33,6 +34,8 @@
 test_computed_value("grid-template-columns", "1px [a] repeat(auto-fit, [b] 2px [c]) [d] 3px");
 test_computed_value("grid-template-columns", "[a] 1px repeat(1, 2px [b] 3px) 4px [d]");
 test_computed_value("grid-template-columns", "[a] 1px repeat(auto-fill, 2px [b] 3px) 4px [d]");
+test_computed_value("grid-template-columns", "[a] 1px 4px [d]");
+test_computed_value("grid-template-columns", "[a] 1px repeat(auto-fill, 2px [b] 3px) [d] 4px");
 test_computed_value("grid-template-columns", "[a] 1px repeat(auto-fit, 2px [b] 3px) 4px [d]");
 test_computed_value("grid-template-rows", "100% [a] repeat(1, [b] 200% [c]) [d] 300%");
 test_computed_value("grid-template-rows", "100% [a] repeat(auto-fill, [b] 200% [c]) [d] 300%");
@@ -40,4 +43,9 @@
 test_computed_value("grid-template-columns", "[a] 1em repeat(1, 2em [b] 3em) 4em [d]", "[a] 1px repeat(1, 2px [b] 3px) 4px [d]");
 test_computed_value("grid-template-columns", "[a] 1em repeat(auto-fill, 2em [b] 3em) 4em [d]", "[a] 1px repeat(auto-fill, 2px [b] 3px) 4px [d]");
 test_computed_value("grid-template-columns", "[a] 1em repeat(auto-fit, 2em [b] 3em) 4em [d]", "[a] 1px repeat(auto-fit, 2px [b] 3px) 4px [d]");
+test_computed_value("grid-template-columns", "repeat(1, 2px [a] 3px) [b] repeat(auto-fill, [c] 200% [d]) [e] 300%");
+test_computed_value("grid-template-columns", "[a] repeat(auto-fill, [b] 200% [c]) repeat(1, 2px [d] 3px) [e] 300%");
+test_computed_value("grid-template-columns", "repeat(1, [a] 2px [b] 3px) [b] repeat(auto-fill, [c] 200% [d]) [e] 300%");
+test_computed_value("grid-template-columns", "[a] repeat(auto-fill, [b] 200% [c]) repeat(1, 2px [d] 3px [e]) [f] 300%");
+test_computed_value("grid-template-columns", "[a] 1px [b c] repeat(auto-fill, [d] 200% [e f]) [g] 2px repeat(1, 3px [d e] 4px [e f]) [g] 300% [h]");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/grid-template-computed-nogrid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/grid-template-computed-nogrid.html
index ccfe67a5..1f37ce8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/grid-template-computed-nogrid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/grid-template-computed-nogrid.html
@@ -48,5 +48,22 @@
 test_computed_value("grid-template-columns", "subgrid [a b] repeat(auto-fill, [c] [d e]) [g]");
 test_computed_value("grid-template-columns", "subgrid [a] [b] repeat(auto-fill, [c] [d e]) [g h]");
 test_computed_value("grid-template-columns", "subgrid [a] [b] repeat(auto-fill, [c] [d e])");
-
+test_computed_value("grid-template-columns", "subgrid [a] repeat(2, [c] [d e])");
+test_computed_value("grid-template-columns", "subgrid repeat(1, [])");
+test_computed_value("grid-template-columns", "subgrid repeat(2, [])");
+test_computed_value("grid-template-columns", "subgrid repeat(2, [a])");
+test_computed_value("grid-template-columns", "subgrid repeat(2, [a] [])");
+test_computed_value("grid-template-columns", "subgrid repeat(2, [] [a] [])");
+test_computed_value("grid-template-columns", "subgrid repeat(2, [] [] []) repeat(auto-fill, [] [] [])");
+test_computed_value("grid-template-columns", "subgrid repeat(1, [a b])");
+test_computed_value("grid-template-columns", "subgrid repeat(2, [a b])");
+test_computed_value("grid-template-columns", "subgrid repeat(1, [a] [b])");
+test_computed_value("grid-template-columns", "subgrid repeat(2, [a] [b])");
+test_computed_value("grid-template-columns", "subgrid [a] repeat(2, [b])");
+test_computed_value("grid-template-columns", "subgrid repeat(2, [a]) [b]");
+test_computed_value("grid-template-columns", "subgrid [a] repeat(2, [b] [c d]) [e]");
+test_computed_value("grid-template-columns", "subgrid repeat(2, [a b]) repeat(auto-fill, [c] [d e])");
+test_computed_value("grid-template-columns", "subgrid repeat(auto-fill, [a] [b c]) repeat(2, [d e])");
+test_computed_value("grid-template-columns", "subgrid repeat(2, [a b]) repeat(auto-fill, [c] [d e]) repeat(2, [f g])");
+test_computed_value("grid-template-columns", "subgrid [a] [b c] repeat(2, [d e]) [f] [g h] repeat(auto-fill, [i] [j k]) [l] repeat(2, [m n]) [o]");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-005-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-005-ref.html
new file mode 100644
index 0000000..f8fa0ab8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-005-ref.html
@@ -0,0 +1,286 @@
+<!DOCTYPE HTML>
+<html><head>
+  <meta charset="utf-8">
+  <title>Reference: line names in orthogonal parent subgrid</title>
+  <link rel="author" title="Kurt Catti-Schmidt" href="mailto:kschmi@microsoft.com">
+  <style>
+    body {
+      font:10px monospace; padding:0; margin:0; line-height:0;
+    }
+
+    .grid {
+      display: inline-grid;
+      grid-auto-columns: 15px;
+      border: 1px solid;
+      vertical-align: bottom;
+    }
+
+    .subgrid {
+      display: grid;
+      grid-template-columns: subgrid;
+      grid-column: 3 / span 4;
+      background: grey;
+    }
+    .grid > .subgrid > .subgrid {
+      writing-mode: vertical-lr;
+      grid-template-rows: subgrid;
+      grid-template-columns: initial;
+      grid-column: 1 / span 4;
+      grid-auto-columns: 8px;
+      grid-auto-flow: column;
+    }
+
+    .fill-1a  { grid-template-columns: subgrid repeat(auto-fill, [y]) [z] [z] [z] [z] }  /* [y] [z] [z] [z] [z] */
+    .fill-1b  { grid-template-columns: subgrid [x] repeat(auto-fill, [y]) [z] [z] [z] }  /* [x] [y] [z] [z] [z] */
+    .fill-1c  { grid-template-columns: subgrid [x] [x] repeat(auto-fill, [y]) [z] [z] }  /* [x] [x] [y] [z] [z] */
+    .fill-1d  { grid-template-columns: subgrid [x] [x] [x] repeat(auto-fill, [y]) [z] }  /* [x] [x] [x] [y] [z] */
+    .fill-1e  { grid-template-columns: subgrid [x] [x] [x] [x] repeat(auto-fill, [y]) }  /* [x] [x] [x] [x] [y] */
+
+    .areas-1a { grid-template-areas: 'x x x x' }
+    .areas-1b { grid-template-areas: '. x x x' }
+    .areas-1c { grid-template-areas: 'x x x .' }
+    .areas-1d { grid-template-areas: '. . x x' }
+    .areas-1e { grid-template-areas: '. . x .' }
+
+    .subgrid > .subgrid > :nth-child(2n)   {  background: black; }
+    .subgrid > .subgrid > :nth-child(2n+1) {  background: pink; }
+    .subgrid > .subgrid > * { writing-mode: horizontal-tb; }
+
+    </style>
+  </head>
+  <body>
+
+  <!-- Line names before/after auto repeat -->
+  <div class="grid"><div class="subgrid fill-1a"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1b"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1c"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1d"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1e"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1a"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <!-- Auto repeat line names -->
+  <div class="grid"><div class="subgrid fill-1b"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1c"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1d"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1e"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1a"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:2"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1b"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:3"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1c"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1d"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid fill-1e"><div class="subgrid">
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:4"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+  </div></div></div>
+
+  <!-- Named grid areas -->
+  <div class="grid"><div class="subgrid areas-1a"><div class="subgrid">
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:2"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid areas-1b"><div class="subgrid">
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:2"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid areas-1c"><div class="subgrid">
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:2"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid areas-1d"><div class="subgrid">
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:2"></div>
+  </div></div></div>
+
+  <div class="grid"><div class="subgrid areas-1e"><div class="subgrid">
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:1"></div>
+    <div style="grid-row:2"></div>
+  </div></div></div>
+  </body>
+  </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-005.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-005.html
new file mode 100644
index 0000000..d5bd178
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-005.html
@@ -0,0 +1,288 @@
+<!DOCTYPE HTML>
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: line names in orthogonal parent subgrid</title>
+  <link rel="author" title="Kurt Catti-Schmidt" href="mailto:kschmi@microsoft.com">
+  <link rel="help" href="https://drafts.csswg.org/css-grid-2/#subgrid-listing">
+  <link rel="match" href="orthogonal-writing-mode-005-ref.html">
+  <style>
+body {
+  color:black; background-color:white; font:10px monospace; padding:0; margin:0; line-height:0;
+}
+
+.grid {
+  display: inline-grid;
+  grid-auto-columns: 15px;
+  border: 1px solid;
+  vertical-align: bottom;
+}
+
+.subgrid {
+  display: grid;
+  grid-template-columns: subgrid;
+  grid-column: 3 / span 4;
+  background: grey;
+}
+.grid > .subgrid > .subgrid {
+  writing-mode: vertical-lr;
+  grid-template-rows: subgrid;
+  grid-template-columns: initial;
+  grid-column: 1 / span 4;
+  grid-auto-columns: 8px;
+  grid-auto-flow: column;
+}
+
+.fill-1a  { grid-template-columns: subgrid repeat(auto-fill, [y]) [z] [z] [z] [z] }  /* [y] [z] [z] [z] [z] */
+.fill-1b  { grid-template-columns: subgrid [x] repeat(auto-fill, [y]) [z] [z] [z] }  /* [x] [y] [z] [z] [z] */
+.fill-1c  { grid-template-columns: subgrid [x] [x] repeat(auto-fill, [y]) [z] [z] }  /* [x] [x] [y] [z] [z] */
+.fill-1d  { grid-template-columns: subgrid [x] [x] [x] repeat(auto-fill, [y]) [z] }  /* [x] [x] [x] [y] [z] */
+.fill-1e  { grid-template-columns: subgrid [x] [x] [x] [x] repeat(auto-fill, [y]) }  /* [x] [x] [x] [x] [y] */
+
+.areas-1a { grid-template-areas: 'x x x x' }
+.areas-1b { grid-template-areas: '. x x x' }
+.areas-1c { grid-template-areas: 'x x x .' }
+.areas-1d { grid-template-areas: '. . x x' }
+.areas-1e { grid-template-areas: '. . x .' }
+
+.subgrid > .subgrid > :nth-child(2n)   {  background: black; }
+.subgrid > .subgrid > :nth-child(2n+1) {  background: pink; }
+.subgrid > .subgrid > * { writing-mode: horizontal-tb; }
+
+  </style>
+</head>
+<body>
+
+<!-- Line names before auto repeat -->
+<div class="grid"><div class="subgrid fill-1a"><div class="subgrid">
+  <div style="grid-row:z 5"></div>
+  <div style="grid-row:z 4"></div>
+  <div style="grid-row:z 3"></div>
+  <div style="grid-row:z 2"></div>
+  <div style="grid-row:z 1"></div>
+  <div style="grid-row:z -1"></div>
+  <div style="grid-row:z -2"></div>
+  <div style="grid-row:z -3"></div>
+  <div style="grid-row:z -4"></div>
+  <div style="grid-row:z -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1b"><div class="subgrid">
+  <div style="grid-row:x 5"></div>
+  <div style="grid-row:x 4"></div>
+  <div style="grid-row:x 3"></div>
+  <div style="grid-row:x 2"></div>
+  <div style="grid-row:x 1"></div>
+  <div style="grid-row:x -1"></div>
+  <div style="grid-row:x -2"></div>
+  <div style="grid-row:x -3"></div>
+  <div style="grid-row:x -4"></div>
+  <div style="grid-row:x -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1c"><div class="subgrid">
+  <div style="grid-row:x 5"></div>
+  <div style="grid-row:x 4"></div>
+  <div style="grid-row:x 3"></div>
+  <div style="grid-row:x 2"></div>
+  <div style="grid-row:x 1"></div>
+  <div style="grid-row:x -1"></div>
+  <div style="grid-row:x -2"></div>
+  <div style="grid-row:x -3"></div>
+  <div style="grid-row:x -4"></div>
+  <div style="grid-row:x -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1d"><div class="subgrid">
+  <div style="grid-row:x 5"></div>
+  <div style="grid-row:x 4"></div>
+  <div style="grid-row:x 3"></div>
+  <div style="grid-row:x 2"></div>
+  <div style="grid-row:x 1"></div>
+  <div style="grid-row:x -1"></div>
+  <div style="grid-row:x -2"></div>
+  <div style="grid-row:x -3"></div>
+  <div style="grid-row:x -4"></div>
+  <div style="grid-row:x -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1e"><div class="subgrid">
+  <div style="grid-row:x 5"></div>
+  <div style="grid-row:x 4"></div>
+  <div style="grid-row:x 3"></div>
+  <div style="grid-row:x 2"></div>
+  <div style="grid-row:x 1"></div>
+  <div style="grid-row:x -1"></div>
+  <div style="grid-row:x -2"></div>
+  <div style="grid-row:x -3"></div>
+  <div style="grid-row:x -4"></div>
+  <div style="grid-row:x -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1a"><div class="subgrid">
+  <div style="grid-row:y 5"></div>
+  <div style="grid-row:y 4"></div>
+  <div style="grid-row:y 3"></div>
+  <div style="grid-row:y 2"></div>
+  <div style="grid-row:y 1"></div>
+  <div style="grid-row:y -1"></div>
+  <div style="grid-row:y -2"></div>
+  <div style="grid-row:y -3"></div>
+  <div style="grid-row:y -4"></div>
+  <div style="grid-row:y -5"></div>
+</div></div></div>
+
+<!-- Auto repeat line names -->
+<div class="grid"><div class="subgrid fill-1b"><div class="subgrid">
+  <div style="grid-row:y 5"></div>
+  <div style="grid-row:y 4"></div>
+  <div style="grid-row:y 3"></div>
+  <div style="grid-row:y 2"></div>
+  <div style="grid-row:y 1"></div>
+  <div style="grid-row:y -1"></div>
+  <div style="grid-row:y -2"></div>
+  <div style="grid-row:y -3"></div>
+  <div style="grid-row:y -4"></div>
+  <div style="grid-row:y -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1c"><div class="subgrid">
+  <div style="grid-row:y 5"></div>
+  <div style="grid-row:y 4"></div>
+  <div style="grid-row:y 3"></div>
+  <div style="grid-row:y 2"></div>
+  <div style="grid-row:y 1"></div>
+  <div style="grid-row:y -1"></div>
+  <div style="grid-row:y -2"></div>
+  <div style="grid-row:y -3"></div>
+  <div style="grid-row:y -4"></div>
+  <div style="grid-row:y -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1d"><div class="subgrid">
+  <div style="grid-row:y 5"></div>
+  <div style="grid-row:y 4"></div>
+  <div style="grid-row:y 3"></div>
+  <div style="grid-row:y 2"></div>
+  <div style="grid-row:y 1"></div>
+  <div style="grid-row:y -1"></div>
+  <div style="grid-row:y -2"></div>
+  <div style="grid-row:y -3"></div>
+  <div style="grid-row:y -4"></div>
+  <div style="grid-row:y -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1e"><div class="subgrid">
+  <div style="grid-row:y 5"></div>
+  <div style="grid-row:y 4"></div>
+  <div style="grid-row:y 3"></div>
+  <div style="grid-row:y 2"></div>
+  <div style="grid-row:y 1"></div>
+  <div style="grid-row:y -1"></div>
+  <div style="grid-row:y -2"></div>
+  <div style="grid-row:y -3"></div>
+  <div style="grid-row:y -4"></div>
+  <div style="grid-row:y -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1a"><div class="subgrid">
+  <div style="grid-row:z 5"></div>
+  <div style="grid-row:z 4"></div>
+  <div style="grid-row:z 3"></div>
+  <div style="grid-row:z 2"></div>
+  <div style="grid-row:z 1"></div>
+  <div style="grid-row:z -1"></div>
+  <div style="grid-row:z -2"></div>
+  <div style="grid-row:z -3"></div>
+  <div style="grid-row:z -4"></div>
+  <div style="grid-row:z -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1b"><div class="subgrid">
+  <div style="grid-row:z 5"></div>
+  <div style="grid-row:z 4"></div>
+  <div style="grid-row:z 3"></div>
+  <div style="grid-row:z 2"></div>
+  <div style="grid-row:z 1"></div>
+  <div style="grid-row:z -1"></div>
+  <div style="grid-row:z -2"></div>
+  <div style="grid-row:z -3"></div>
+  <div style="grid-row:z -4"></div>
+  <div style="grid-row:z -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1c"><div class="subgrid">
+  <div style="grid-row:z 5"></div>
+  <div style="grid-row:z 4"></div>
+  <div style="grid-row:z 3"></div>
+  <div style="grid-row:z 2"></div>
+  <div style="grid-row:z 1"></div>
+  <div style="grid-row:z -1"></div>
+  <div style="grid-row:z -2"></div>
+  <div style="grid-row:z -3"></div>
+  <div style="grid-row:z -4"></div>
+  <div style="grid-row:z -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1d"><div class="subgrid">
+  <div style="grid-row:z 5"></div>
+  <div style="grid-row:z 4"></div>
+  <div style="grid-row:z 3"></div>
+  <div style="grid-row:z 2"></div>
+  <div style="grid-row:z 1"></div>
+  <div style="grid-row:z -1"></div>
+  <div style="grid-row:z -2"></div>
+  <div style="grid-row:z -3"></div>
+  <div style="grid-row:z -4"></div>
+  <div style="grid-row:z -5"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid fill-1e"><div class="subgrid">
+  <div style="grid-row:z 5"></div>
+  <div style="grid-row:z 4"></div>
+  <div style="grid-row:z 3"></div>
+  <div style="grid-row:z 2"></div>
+  <div style="grid-row:z 1"></div>
+  <div style="grid-row:z -1"></div>
+  <div style="grid-row:z -2"></div>
+  <div style="grid-row:z -3"></div>
+  <div style="grid-row:z -4"></div>
+  <div style="grid-row:z -5"></div>
+</div></div></div>
+
+<!-- Named grid areas -->
+<div class="grid"><div class="subgrid areas-1a"><div class="subgrid">
+  <div style="grid-row:x-start"></div>
+  <div style="grid-row:x"></div>
+  <div style="grid-row:x-start / x-end"></div>
+  <div style="grid-row:x-end"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid areas-1b"><div class="subgrid">
+  <div style="grid-row:x-start"></div>
+  <div style="grid-row:x"></div>
+  <div style="grid-row:x-start / x-end"></div>
+  <div style="grid-row:x-end"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid areas-1c"><div class="subgrid">
+  <div style="grid-row:x-start"></div>
+  <div style="grid-row:x"></div>
+  <div style="grid-row:x-start / x-end"></div>
+  <div style="grid-row:x-end"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid areas-1d"><div class="subgrid">
+  <div style="grid-row:x-start"></div>
+  <div style="grid-row:x"></div>
+  <div style="grid-row:x-start / x-end"></div>
+  <div style="grid-row:x-end"></div>
+</div></div></div>
+
+<div class="grid"><div class="subgrid areas-1e"><div class="subgrid">
+  <div style="grid-row:x-start"></div>
+  <div style="grid-row:x"></div>
+  <div style="grid-row:x-start / x-end"></div>
+  <div style="grid-row:x-end"></div>
+</div></div></div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/repeat-auto-fill-003.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/repeat-auto-fill-003.html
index 3a1d270..df14ed8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/repeat-auto-fill-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/repeat-auto-fill-003.html
@@ -11,13 +11,14 @@
   <link rel="match" href="repeat-auto-fill-001-ref.html">
   <style>
 html,body {
-  color:black; background-color:white; font:10px/1 monospace; padding:0; margin:0;
+  color:black; background-color:white; font:10px monospace; padding:0; margin:0; line-height:0;
 }
 
 .grid {
   display: inline-grid;
   grid-auto-columns: 15px;
   border: 1px solid;
+  vertical-align: top;
 }
 
 .subgrid {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-inline/alignment-baseline-no-interpolation.html
similarity index 88%
copy from third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
copy to third_party/blink/web_tests/external/wpt/css/css-inline/alignment-baseline-no-interpolation.html
index a1a48d5..57deaf8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-inline/alignment-baseline-no-interpolation.html
@@ -8,8 +8,8 @@
 <body>
 <script>
 test_no_interpolation({
-  property: 'background-repeat',
+  property: 'alignment-baseline',
   from: 'initial',
-  to: 'round'
+  to: 'central'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-inline/baseline-source/baseline-source-no-interpolation.html
similarity index 88%
copy from third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
copy to third_party/blink/web_tests/external/wpt/css/css-inline/baseline-source/baseline-source-no-interpolation.html
index a1a48d5..86fede73 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-inline/baseline-source/baseline-source-no-interpolation.html
@@ -8,8 +8,8 @@
 <body>
 <script>
 test_no_interpolation({
-  property: 'background-repeat',
+  property: 'baseline-source',
   from: 'initial',
-  to: 'round'
+  to: 'last'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-logical/animations/caption-side-no-interpolation.html
similarity index 88%
copy from third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
copy to third_party/blink/web_tests/external/wpt/css/css-logical/animations/caption-side-no-interpolation.html
index a1a48d5..2eef093 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-logical/animations/caption-side-no-interpolation.html
@@ -8,8 +8,8 @@
 <body>
 <script>
 test_no_interpolation({
-  property: 'background-repeat',
+  property: 'caption-side',
   from: 'initial',
-  to: 'round'
+  to: 'bottom'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-rule/clip-rule-no-interpolation.html
similarity index 88%
copy from third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
copy to third_party/blink/web_tests/external/wpt/css/css-masking/clip-rule/clip-rule-no-interpolation.html
index a1a48d5..066636c5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-rule/clip-rule-no-interpolation.html
@@ -8,8 +8,8 @@
 <body>
 <script>
 test_no_interpolation({
-  property: 'background-repeat',
+  property: 'clip-rule',
   from: 'initial',
-  to: 'round'
+  to: 'evenodd'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/animation/box-sizing-no-interpolation.html
similarity index 88%
copy from third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
copy to third_party/blink/web_tests/external/wpt/css/css-sizing/animation/box-sizing-no-interpolation.html
index a1a48d5..d688445 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/animation/box-sizing-no-interpolation.html
@@ -8,8 +8,8 @@
 <body>
 <script>
 test_no_interpolation({
-  property: 'background-repeat',
+  property: 'box-sizing',
   from: 'initial',
-  to: 'round'
+  to: 'border-box'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/backface-visibility-no-interpolation.html
similarity index 88%
copy from third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
copy to third_party/blink/web_tests/external/wpt/css/css-transforms/animation/backface-visibility-no-interpolation.html
index a1a48d5..b7b9f2d5f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/backface-visibility-no-interpolation.html
@@ -8,8 +8,8 @@
 <body>
 <script>
 test_no_interpolation({
-  property: 'background-repeat',
+  property: 'backface-visibility',
   from: 'initial',
-  to: 'round'
+  to: 'hidden'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-ui/animation/appearance-no-interpolation.html
similarity index 88%
copy from third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
copy to third_party/blink/web_tests/external/wpt/css/css-ui/animation/appearance-no-interpolation.html
index a1a48d5..5ddfcd4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/background-repeat/background-repeat-no-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/animation/appearance-no-interpolation.html
@@ -8,8 +8,8 @@
 <body>
 <script>
 test_no_interpolation({
-  property: 'background-repeat',
+  property: 'appearance',
   from: 'initial',
-  to: 'round'
+  to: 'none'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/dom/events/Event-dispatch-on-disabled-elements.html b/third_party/blink/web_tests/external/wpt/dom/events/Event-dispatch-on-disabled-elements.html
index 361006a..e7d6b455 100644
--- a/third_party/blink/web_tests/external/wpt/dom/events/Event-dispatch-on-disabled-elements.html
+++ b/third_party/blink/web_tests/external/wpt/dom/events/Event-dispatch-on-disabled-elements.html
@@ -19,7 +19,7 @@
 <body>
 <script>
 // HTML elements that can be disabled
-const formElements = ["button", "fieldset", "input", "select", "textarea"];
+const formElements = ["button", "input", "select", "textarea"];
 
 test(() => {
   for (const localName of formElements) {
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/disabled-elements/fieldset-event-propagation.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/disabled-elements/fieldset-event-propagation.tentative.html
new file mode 100644
index 0000000..11822e4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/disabled-elements/fieldset-event-propagation.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-disabled">
+<link rel=help href="https://github.com/whatwg/html/issues/5886#issuecomment-1460425364">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+
+<div id=target1parent>
+  <fieldset disabled id=target1fieldset>
+    <div id=target1child>hello world</div>
+  </fieldset>
+</div>
+
+<div id=target2parent>
+  <fieldset disabled id=target2fieldset>hello world</div>
+</div>
+
+<script>
+promise_test(async () => {
+  let target1parentClicked = false;
+  let target1childClicked = false;
+  let target1fieldsetClicked = false;
+  target1parent.onclick = () => target1parentClicked = true;
+  target1child.onclick = () => target1childClicked = true;
+  target1fieldset.onclick = () => target1fieldsetClicked = true;
+
+  await test_driver.click(target1child);
+
+  assert_true(target1parentClicked, 'The parent of the fieldset should receive a click event.');
+  assert_true(target1childClicked, 'The child of the fieldset should receive a click event.');
+  assert_true(target1fieldsetClicked, 'The fieldset element should receive a click event.');
+}, 'Disabled fieldset elements should not prevent click event propagation.');
+
+promise_test(async () => {
+  let target2parentClicked = false;
+  let target2fieldsetClicked = false;
+  target2parent.onclick = () => target2parentClicked = true;
+  target2fieldset.onclick = () => target2fieldsetClicked = true;
+
+  await test_driver.click(target2fieldset);
+
+  assert_true(target2parentClicked, 'The parent of the fieldset should receive a click event.');
+  assert_true(target2fieldsetClicked, 'The fieldset element should receive a click event.');
+}, 'Disabled fieldset elements should not block click events.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-pseudo-open-closed.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-pseudo-open-closed.tentative.html
new file mode 100644
index 0000000..b1400e0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-pseudo-open-closed.tentative.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/547">
+<link rel=help href="https://drafts.csswg.org/selectors/#open-state">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectmenu id=myselectmenu>
+  <button id=custombutton slot=button behavior=button>button</button>
+  <option>one</option>
+  <option>two</option>
+</selectmenu>
+
+<script>
+test(() => {
+  assert_false(myselectmenu.matches(':open'),
+    'Selectmenu should not match :open while it is closed.');
+  assert_true(myselectmenu.matches(':closed'),
+    'Selectmenu should match :closed while it is closed.');
+
+  custombutton.click();
+
+  assert_true(myselectmenu.matches(':open'),
+    'Selectmenu should match :open while it is open.');
+  assert_false(myselectmenu.matches(':closed'),
+    'Selectmenu should not match :closed while it is open.');
+}, 'Selectmenu should support :open and :closed pseudo selectors.');
+</script>
+
+<selectmenu id=selectmenuinvalidation>
+  <button slot=button behavior=button>button</button>
+  <option>one</option>
+  <option>two</option>
+</selectmenu>
+<style>
+selectmenu:closed {
+  background-color: red;
+}
+selectmenu:open {
+  background-color: green;
+}
+</style>
+
+<script>
+test(() => {
+  const selectmenu = document.getElementById('selectmenuinvalidation');
+  const button = selectmenu.querySelector('button');
+  const option = selectmenu.querySelector('option');
+
+  assert_equals(getComputedStyle(selectmenu).backgroundColor, 'rgb(255, 0, 0)',
+    'The style rules from :closed should apply when the selectmenu is closed.');
+
+  button.click();
+  assert_equals(getComputedStyle(selectmenu).backgroundColor, 'rgb(0, 128, 0)',
+    'The style rules from :open should apply when the selectmenu is open.');
+
+  option.click();
+  assert_equals(getComputedStyle(selectmenu).backgroundColor, 'rgb(255, 0, 0)',
+    'The style rules from :closed should apply when the selectmenu is opened and closed again.');
+}, 'Selectmenu :open and :closed should invalidate correctly.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/selectors/pseudo-classes/disabled.html b/third_party/blink/web_tests/external/wpt/html/semantics/selectors/pseudo-classes/disabled.html
index 8808675..f960043 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/selectors/pseudo-classes/disabled.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/selectors/pseudo-classes/disabled.html
@@ -26,7 +26,6 @@
 </select>
 <textarea id=textarea1>textarea1</textarea>
 <textarea disabled id=textarea2>textarea2</textarea>
-<fieldset id=fieldset1></fieldset>
 <fieldset disabled id=fieldset2>
   <legend><input type=checkbox id=club></legend>
   <p><label>Name on card: <input id=clubname required></label></p>
@@ -40,21 +39,21 @@
 <progress disabled></progress>
 
 <script>
-  testSelectorIdsMatch(":disabled", ["button2", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should match only disabled elements");
+  testSelectorIdsMatch(":disabled", ["button2", "input2", "select2", "optgroup2", "option2", "textarea2", "clubname", "clubnum"], "':disabled' should match only disabled elements");
 
   document.getElementById("button2").removeAttribute("disabled");
-  testSelectorIdsMatch(":disabled", ["input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should not match elements whose disabled attribute has been removed");
+  testSelectorIdsMatch(":disabled", ["input2", "select2", "optgroup2", "option2", "textarea2", "clubname", "clubnum"], "':disabled' should not match elements whose disabled attribute has been removed");
 
   document.getElementById("button1").setAttribute("disabled", "disabled");
-  testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set");
+  testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set");
 
   document.getElementById("button1").setAttribute("disabled", "disabled");
-  testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set twice");
+  testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set twice");
 
   document.getElementById("input2").setAttribute("type", "submit"); // change input type to submit
-  testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match disabled elements whose type has changed");
+  testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "clubname", "clubnum"], "':disabled' should also match disabled elements whose type has changed");
 
   var input = document.createElement("input");
   input.setAttribute("disabled", "disabled");
-  testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should not match elements not in the document");
+  testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "clubname", "clubnum"], "':disabled' should not match elements not in the document");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/selectors/pseudo-classes/enabled.html b/third_party/blink/web_tests/external/wpt/html/semantics/selectors/pseudo-classes/enabled.html
index 0ad0e1b40..1948343 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/selectors/pseudo-classes/enabled.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/selectors/pseudo-classes/enabled.html
@@ -36,7 +36,6 @@
  </menu>
 </form>
 <fieldset id=fieldset1></fieldset>
-<fieldset disabled id=fieldset2></fieldset>
 
 <script>
   testSelectorIdsMatch(":enabled", ["button1", "input1", "select1", "optgroup1", "option1", "textarea1", "submitbutton", "fieldset1"], "':enabled' elements that are not disabled");
diff --git a/third_party/blink/web_tests/fast/css/invalidation/fieldset-disabled-expected.txt b/third_party/blink/web_tests/fast/css/invalidation/fieldset-disabled-expected.txt
deleted file mode 100644
index 4796425..0000000
--- a/third_party/blink/web_tests/fast/css/invalidation/fieldset-disabled-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Use descendant invalidation set for :disabled fieldset.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS getComputedStyle(fieldset, '').backgroundColor is transparent
-PASS internals.updateStyleAndReturnAffectedElementCount() is 2
-PASS getComputedStyle(fieldset, '').backgroundColor is green
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/fast/css/invalidation/fieldset-disabled.html b/third_party/blink/web_tests/fast/css/invalidation/fieldset-disabled.html
index c96deb1..02316b4 100644
--- a/third_party/blink/web_tests/fast/css/invalidation/fieldset-disabled.html
+++ b/third_party/blink/web_tests/fast/css/invalidation/fieldset-disabled.html
@@ -1,13 +1,15 @@
 <!DOCTYPE html>
-<script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
 <style>
-fieldset:disabled { background-color: green }
+button:disabled { background-color: green }
 fieldset + div { color: pink }
 </style>
 <fieldset id="fieldset">
     <legend></legend>
     <label for="field"></label>
     <input type="text" name="field" id="field">
+    <button id=button></button>
     <div>
         <div></div>
         <div></div>
@@ -22,21 +24,23 @@
     <div></div>
 </div>
 <script>
-description("Use descendant invalidation set for :disabled fieldset.")
+test(() => {
+  const transparent = "rgba(0, 0, 0, 0)";
+  const green = "rgb(0, 128, 0)";
 
-var transparent = "rgba(0, 0, 0, 0)";
-var green = "rgb(0, 128, 0)";
+  assert_equals(getComputedStyle(fieldset, '').backgroundColor, transparent);
 
-shouldBe("getComputedStyle(fieldset, '').backgroundColor", "transparent");
+  fieldset.offsetTop; // Force recalc.
+  fieldset.disabled = true;
 
-fieldset.offsetTop; // Force recalc.
-fieldset.disabled = true;
+  if (window.internals) {
+      // There are still instances of SubtreeStyleChange left when updating
+      // disabled state. This count should become lower.
+      assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 3);
+  }
 
-if (window.internals) {
-    // There are still instances of SubtreeStyleChange left when updating
-    // disabled state. This count should become lower.
-    shouldBe("internals.updateStyleAndReturnAffectedElementCount()", "2");
-}
-
-shouldBe("getComputedStyle(fieldset, '').backgroundColor", "green");
+  // Fieldsets can't be disabled or match :disabled, so we have to test a
+  // descendant element which becomes disabled by making the fieldset disabled.
+  assert_equals(getComputedStyle(button, '').backgroundColor, green);
+}, "Use descendant invalidation set for :disabled fieldset.");
 </script>
diff --git a/third_party/blink/web_tests/fast/forms/fieldset/fieldset-disabled-expected.txt b/third_party/blink/web_tests/fast/forms/fieldset/fieldset-disabled-expected.txt
deleted file mode 100644
index 0057f90..0000000
--- a/third_party/blink/web_tests/fast/forms/fieldset/fieldset-disabled-expected.txt
+++ /dev/null
@@ -1,94 +0,0 @@
-Tests for HTMLFieldSetElement.disabled behavior.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-Verifying parser generated fieldsets.
-PASS parserGeneratedInput1.value is "L"
-PASS parserGeneratedInput2.value is "M"
-PASS parserGeneratedInput3.value is "NO"
-PASS parserGeneratedInput4.value is ""
-PASS parserGeneratedInput5.value is "PQRST"
-PASS parserGeneratedInput6.value is ""
-PASS parserGeneratedInput7.value is ""
-PASS parserGeneratedInput8.value is ""
-PASS parserGeneratedInput9.value is ""
-
-Testing a single fieldset element.
-Verifying HTMLFormControl can be disabled regardless of enclosing fieldset.
-PASS textInput.disabled is true
-PASS textInput.value is ""
-PASS fieldSet.disabled is false
-Fieldset is enabled by default. A user can insertText into the text input field.
-PASS textInput.value is "A"
-Disable fieldset.
-PASS fieldSet.disabled is true
-Once the fieldset is disabled, text cannot be inserted.
-PASS textInput.value is "A"
-Check if the style of the text element changed.
-PASS getComputedStyle(textInput).backgroundColor is 'rgb(255, 0, 0)'
-Enable fieldset.
-PASS fieldSet.disabled is false
-PASS getComputedStyle(textInput).backgroundColor is 'rgb(255, 255, 100)'
-PASS textInput.value is "AB"
-Move the textinput element out of the fieldset.
-Disable the fieldset.
-PASS fieldSet.disabled is true
-Text can be inserted, because the textinput element is outside of the disabled fieldset.
-PASS textInput.value is "ABC"
-Enable the fieldset.
-PASS fieldSet.disabled is false
-Insert a table into the fieldset.
-Move the textinput field into the table.
-PASS textInput.value is "ABCD"
-Disable the fieldset.
-PASS fieldSet.disabled is true
-Inserting text should fail.
-PASS textInput.value is "ABCD"
-Enable the fieldset.
-PASS fieldSet.disabled is false
-PASS textInput.value is "ABCDE"
-
-Testing nested fieldset elements.
-Verifying that subordinates of both fieldsets are enabled.
-PASS outerTextInput.value is "F"
-PASS innerTextInput.value is "F"
-Disabling the inner fieldset only.
-PASS innerFieldSet.disabled is true
-PASS outerTextInput.value is "FGG"
-PASS innerTextInput.value is "F"
-Enabling the inner and disabling the outer fieldset.
-PASS outerFieldSet.disabled is true
-PASS innerFieldSet.disabled is false
-PASS outerTextInput.value is "FGG"
-PASS innerTextInput.value is "F"
-Disabling both fieldset elements.
-PASS outerFieldSet.disabled is true
-PASS innerFieldSet.disabled is true
-PASS outerTextInput.value is "FGG"
-PASS innerTextInput.value is "F"
-Enabling both fieldset elements.
-PASS outerFieldSet.disabled is false
-PASS innerFieldSet.disabled is false
-PASS outerTextInput.value is "FGGH"
-PASS innerTextInput.value is "FH"
-
-Test behavior of the first legend element in a fieldset elements.
-Children of the first legend element in a fieldset should not get disabled with the fieldset.
-PASS legendFieldSet.disabled is true
-PASS firstLegendTextInput.value is "II"
-PASS secondLegendTextInput.value is ""
-Insert another legend element before the currently first one, and check again.
-PASS insertedLegendTextInput.value is "JJJ"
-PASS firstLegendTextInput.value is "II"
-PASS secondLegendTextInput.value is ""
-Enable the fieldset again and check for sanity.
-PASS legendFieldSet.disabled is false
-PASS insertedLegendTextInput.value is "JJJK"
-PASS firstLegendTextInput.value is "IIK"
-PASS secondLegendTextInput.value is "K"
-PASS disabledFieldsetWithTabindex.focus(); document.activeElement is document.body
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/fast/forms/fieldset/fieldset-disabled.html b/third_party/blink/web_tests/fast/forms/fieldset/fieldset-disabled.html
index d730dda..c5cd128 100644
--- a/third_party/blink/web_tests/fast/forms/fieldset/fieldset-disabled.html
+++ b/third_party/blink/web_tests/fast/forms/fieldset/fieldset-disabled.html
@@ -1,7 +1,8 @@
 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html>
 <head>
-<script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
 <style type="text/css">
 input {
     background:rgb(255,255,100);
@@ -33,9 +34,9 @@
 </form>
 <fieldset tabindex=0 disabled id="fieldset-tabindex"></fieldset>
 <script>
-description('Tests for HTMLFieldSetElement.disabled behavior.');
+test(() => {
 
-debug('\nVerifying parser generated fieldsets.');
+// Verifying parser generated fieldsets.
 var parserGeneratedInput1 = document.getElementById("parserGeneratedInput1");
 var parserGeneratedInput2 = document.getElementById("parserGeneratedInput2");
 var parserGeneratedInput3 = document.getElementById("parserGeneratedInput3");
@@ -65,77 +66,77 @@
 parserGeneratedInput9.focus();
 document.execCommand('insertText', false, 'T');
 
-shouldBe('parserGeneratedInput1.value', '"L"');
-shouldBe('parserGeneratedInput2.value', '"M"');
-shouldBe('parserGeneratedInput3.value', '"NO"');
-shouldBe('parserGeneratedInput4.value', '""');
-shouldBe('parserGeneratedInput5.value', '"PQRST"');
-shouldBe('parserGeneratedInput6.value', '""');
-shouldBe('parserGeneratedInput7.value', '""');
-shouldBe('parserGeneratedInput8.value', '""');
-shouldBe('parserGeneratedInput9.value', '""');
+assert_equals(parserGeneratedInput1.value, 'L');
+assert_equals(parserGeneratedInput2.value, 'M');
+assert_equals(parserGeneratedInput3.value, 'NO');
+assert_equals(parserGeneratedInput4.value, '');
+assert_equals(parserGeneratedInput5.value, 'PQRST');
+assert_equals(parserGeneratedInput6.value, '');
+assert_equals(parserGeneratedInput7.value, '');
+assert_equals(parserGeneratedInput8.value, '');
+assert_equals(parserGeneratedInput9.value, '');
 
 
-debug('\nTesting a single fieldset element.');
+// Testing a single fieldset element.
 var fieldSet = document.createElement('fieldset');
 document.body.appendChild(fieldSet);
 var textInput = document.createElement('input');
 textInput.type = "text";
 fieldSet.appendChild(textInput);
 
-debug('Verifying HTMLFormControl can be disabled regardless of enclosing fieldset.');
+// Verifying HTMLFormControl can be disabled regardless of enclosing fieldset.
 textInput.disabled = true;
-shouldBeTrue('textInput.disabled');
+assert_true(textInput.disabled);
 textInput.focus();
 document.execCommand('insertText', false, 'A');
-shouldBe('textInput.value', '""');
-shouldBeFalse('fieldSet.disabled');
+assert_equals(textInput.value, '');
+assert_false(fieldSet.disabled);
 textInput.disabled = false;
 
-debug('Fieldset is enabled by default. A user can insertText into the text input field.');
+// Fieldset is enabled by default. A user can insertText into the text input field.
 textInput.focus();
 document.execCommand('insertText', false, 'A');
-shouldBe('textInput.value', '"A"');
+assert_equals(textInput.value, 'A');
 
-debug('Disable fieldset.');
+// Disable fieldset.
 fieldSet.disabled = true;
-shouldBeTrue('fieldSet.disabled');
+assert_true(fieldSet.disabled);
 
-debug('Once the fieldset is disabled, text cannot be inserted.');
+// Once the fieldset is disabled, text cannot be inserted.
 textInput.focus();
 document.execCommand('insertText', false, 'B');
-shouldBe('textInput.value', '"A"');
+assert_equals(textInput.value, 'A');
 
-debug("Check if the style of the text element changed.");
-shouldBe("getComputedStyle(textInput).backgroundColor", "'rgb(255, 0, 0)'");
+// Check if the style of the text element changed.
+assert_equals(getComputedStyle(textInput).backgroundColor, 'rgb(255, 0, 0)');
 
-debug('Enable fieldset.');
+// Enable fieldset.
 fieldSet.disabled = false;
-shouldBeFalse('fieldSet.disabled');
-shouldBe("getComputedStyle(textInput).backgroundColor", "'rgb(255, 255, 100)'");
+assert_false(fieldSet.disabled);
+assert_equals(getComputedStyle(textInput).backgroundColor, 'rgb(255, 255, 100)');
 
 textInput.focus();
 document.execCommand('insertText', false, 'B');
-shouldBe('textInput.value', '"AB"');
+assert_equals(textInput.value, 'AB');
 
-debug('Move the textinput element out of the fieldset.');
+// Move the textinput element out of the fieldset.
 fieldSet.removeChild(textInput);
 document.body.appendChild(textInput);
 
-debug('Disable the fieldset.');
+// Disable the fieldset.
 fieldSet.disabled = true;
-shouldBeTrue('fieldSet.disabled');
+assert_true(fieldSet.disabled);
 
-debug('Text can be inserted, because the textinput element is outside of the disabled fieldset.');
+// Text can be inserted, because the textinput element is outside of the disabled fieldset.
 textInput.focus();
 document.execCommand('insertText', false, 'C');
-shouldBe('textInput.value', '"ABC"');
+assert_equals(textInput.value, 'ABC');
 
-debug('Enable the fieldset.');
+// Enable the fieldset.
 fieldSet.disabled = false;
-shouldBeFalse('fieldSet.disabled');
+assert_false(fieldSet.disabled);
 
-debug('Insert a table into the fieldset.');
+// Insert a table into the fieldset.
 var table = document.createElement('table');
 fieldSet.appendChild(table);
 var tr = document.createElement('tr');
@@ -143,33 +144,33 @@
 var td = document.createElement('td');
 tr.appendChild(td);
 
-debug('Move the textinput field into the table.');
+// Move the textinput field into the table.
 document.body.removeChild(textInput);
 td.appendChild(textInput);
 
 textInput.focus();
 document.execCommand('insertText', false, 'D');
-shouldBe('textInput.value', '"ABCD"');
+assert_equals(textInput.value, 'ABCD');
 
-debug('Disable the fieldset.');
+// Disable the fieldset.
 fieldSet.disabled = true;
-shouldBeTrue('fieldSet.disabled');
+assert_true(fieldSet.disabled);
 
-debug('Inserting text should fail.')
+// Inserting text should fail.
 textInput.focus();
 document.execCommand('insertText', false, 'E');
-shouldBe('textInput.value', '"ABCD"');
+assert_equals(textInput.value, 'ABCD');
 
-debug('Enable the fieldset.');
+// Enable the fieldset.
 fieldSet.disabled = false;
-shouldBeFalse('fieldSet.disabled');
+assert_false(fieldSet.disabled);
 
 textInput.focus();
 document.execCommand('insertText', false, 'E');
-shouldBe('textInput.value', '"ABCDE"');
+assert_equals(textInput.value, 'ABCDE');
 
 
-debug('\nTesting nested fieldset elements.');
+// Testing nested fieldset elements.
 var outerFieldSet = document.createElement('fieldset');
 document.body.appendChild(outerFieldSet);
 var innerFieldSet = document.createElement('fieldset');
@@ -181,62 +182,62 @@
 innerTextInput.type = "text";
 innerFieldSet.appendChild(innerTextInput);
 
-debug('Verifying that subordinates of both fieldsets are enabled.');
+// Verifying that subordinates of both fieldsets are enabled.
 outerTextInput.focus();
 document.execCommand('insertText', false, 'F');
 innerTextInput.focus();
 document.execCommand('insertText', false, 'F');
-shouldBe('outerTextInput.value', '"F"');
-shouldBe('innerTextInput.value', '"F"');
+assert_equals(outerTextInput.value, 'F');
+assert_equals(innerTextInput.value, 'F');
 
-debug('Disabling the inner fieldset only.');
+// Disabling the inner fieldset only.
 innerFieldSet.disabled = true;
-shouldBeTrue('innerFieldSet.disabled');
+assert_true(innerFieldSet.disabled);
 outerTextInput.focus();
 document.execCommand('insertText', false, 'G');
 innerTextInput.focus();
 document.execCommand('insertText', false, 'G');
-shouldBe('outerTextInput.value', '"FGG"');
-shouldBe('innerTextInput.value', '"F"');
+assert_equals(outerTextInput.value, 'FGG');
+assert_equals(innerTextInput.value, 'F');
 
-debug('Enabling the inner and disabling the outer fieldset.');
+// Enabling the inner and disabling the outer fieldset.
 outerFieldSet.disabled = true;
 innerFieldSet.disabled = false;
-shouldBeTrue('outerFieldSet.disabled');
-shouldBeFalse('innerFieldSet.disabled');
+assert_true(outerFieldSet.disabled);
+assert_false(innerFieldSet.disabled);
 outerTextInput.focus();
 document.execCommand('insertText', false, 'H');
 innerTextInput.focus();
 document.execCommand('insertText', false, 'H');
-shouldBe('outerTextInput.value', '"FGG"');
-shouldBe('innerTextInput.value', '"F"');
+assert_equals(outerTextInput.value, 'FGG');
+assert_equals(innerTextInput.value, 'F');
 
-debug('Disabling both fieldset elements.');
+// Disabling both fieldset elements.
 outerFieldSet.disabled = true;
 innerFieldSet.disabled = true;
-shouldBeTrue('outerFieldSet.disabled');
-shouldBeTrue('innerFieldSet.disabled');
+assert_true(outerFieldSet.disabled);
+assert_true(innerFieldSet.disabled);
 outerTextInput.focus();
 document.execCommand('insertText', false, 'H');
 innerTextInput.focus();
 document.execCommand('insertText', false, 'H');
-shouldBe('outerTextInput.value', '"FGG"');
-shouldBe('innerTextInput.value', '"F"');
+assert_equals(outerTextInput.value, 'FGG');
+assert_equals(innerTextInput.value, 'F');
 
-debug('Enabling both fieldset elements.');
+// Enabling both fieldset elements.
 outerFieldSet.disabled = false;
 innerFieldSet.disabled = false;
-shouldBeFalse('outerFieldSet.disabled');
-shouldBeFalse('innerFieldSet.disabled');
+assert_false(outerFieldSet.disabled);
+assert_false(innerFieldSet.disabled);
 outerTextInput.focus();
 document.execCommand('insertText', false, 'H');
 innerTextInput.focus();
 document.execCommand('insertText', false, 'H');
-shouldBe('outerTextInput.value', '"FGGH"');
-shouldBe('innerTextInput.value', '"FH"');
+assert_equals(outerTextInput.value, 'FGGH');
+assert_equals(innerTextInput.value, 'FH');
 
 
-debug('\nTest behavior of the first legend element in a fieldset elements.');
+// Test behavior of the first legend element in a fieldset elements.
 var legendFieldSet = document.createElement('fieldset');
 document.body.appendChild(legendFieldSet);
 var firstLegend = document.createElement('legend');
@@ -252,17 +253,17 @@
 secondLegendTextInput.type = "text";
 secondLegend.appendChild(secondLegendTextInput);
 
-debug('Children of the first legend element in a fieldset should not get disabled with the fieldset.');
+// Children of the first legend element in a fieldset should not get disabled with the fieldset.
 legendFieldSet.disabled = true;
-shouldBeTrue('legendFieldSet.disabled');
+assert_true(legendFieldSet.disabled);
 firstLegendTextInput.focus();
 document.execCommand('insertText', false, 'I');
 secondLegendTextInput.focus()
 document.execCommand('insertText', false, 'I');
-shouldBe('firstLegendTextInput.value', '"II"');
-shouldBe('secondLegendTextInput.value', '""');
+assert_equals(firstLegendTextInput.value, 'II');
+assert_equals(secondLegendTextInput.value, '');
 
-debug('Insert another legend element before the currently first one, and check again.');
+// Insert another legend element before the currently first one, and check again.
 var insertedLegend = document.createElement('legend');
 legendFieldSet.insertBefore(insertedLegend, firstLegend);
 var insertedLegendTextInput = document.createElement('input');
@@ -274,26 +275,27 @@
 document.execCommand('insertText', false, 'J');
 secondLegendTextInput.focus()
 document.execCommand('insertText', false, 'J');
-shouldBe('insertedLegendTextInput.value', '"JJJ"');
-shouldBe('firstLegendTextInput.value', '"II"');
-shouldBe('secondLegendTextInput.value', '""');
+assert_equals(insertedLegendTextInput.value, 'JJJ');
+assert_equals(firstLegendTextInput.value, 'II');
+assert_equals(secondLegendTextInput.value, '');
 
-debug('Enable the fieldset again and check for sanity.');
+// Enable the fieldset again and check for sanity.
 legendFieldSet.disabled = false;
-shouldBeFalse('legendFieldSet.disabled');
+assert_false(legendFieldSet.disabled);
 insertedLegendTextInput.focus();
 document.execCommand('insertText', false, 'K');
 firstLegendTextInput.focus();
 document.execCommand('insertText', false, 'K');
 secondLegendTextInput.focus()
 document.execCommand('insertText', false, 'K');
-shouldBe('insertedLegendTextInput.value', '"JJJK"');
-shouldBe('firstLegendTextInput.value', '"IIK"');
-shouldBe('secondLegendTextInput.value', '"K"');
+assert_equals(insertedLegendTextInput.value, 'JJJK');
+assert_equals(firstLegendTextInput.value, 'IIK');
+assert_equals(secondLegendTextInput.value, 'K');
 
 var disabledFieldsetWithTabindex = document.getElementById('fieldset-tabindex');
 document.activeElement.blur();
-shouldBe('disabledFieldsetWithTabindex.focus(); document.activeElement', 'document.body');
+disabledFieldsetWithTabindex.focus();
+assert_equals(document.activeElement, disabledFieldsetWithTabindex);
 
 document.body.removeChild(document.getElementsByTagName('form')[0]);
 document.body.removeChild(fieldSet);
@@ -301,6 +303,7 @@
 document.body.removeChild(legendFieldSet);
 document.body.removeChild(disabledFieldsetWithTabindex);
 var successfullyParsed = true;
+}, 'Tests for HTMLFieldSetElement.disabled behavior.');
 </script>
 
 </body>
diff --git a/third_party/closure_compiler/externs/runtime.js b/third_party/closure_compiler/externs/runtime.js
index e6e6935..bc5666d 100644
--- a/third_party/closure_compiler/externs/runtime.js
+++ b/third_party/closure_compiler/externs/runtime.js
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors
+// Copyright 2023 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -160,6 +160,34 @@
 };
 
 /**
+ * @enum {string}
+ * @see https://developer.chrome.com/extensions/runtime#type-ContextType
+ */
+chrome.runtime.ContextType = {
+  TAB: 'TAB',
+  POPUP: 'POPUP',
+  BACKGROUND: 'BACKGROUND',
+  OFFSCREEN_DOCUMENT: 'OFFSCREEN_DOCUMENT',
+};
+
+/**
+ * A context hosting extension content.
+ * @typedef {{
+ *   contextType: !chrome.runtime.ContextType,
+ *   contextId: string,
+ *   tabId: number,
+ *   windowId: number,
+ *   documentId: (string|undefined),
+ *   frameId: number,
+ *   documentUrl: (string|undefined),
+ *   documentOrigin: (string|undefined),
+ *   incognito: boolean
+ * }}
+ * @see https://developer.chrome.com/extensions/runtime#type-ExtensionContext
+ */
+chrome.runtime.ExtensionContext;
+
+/**
  * This will be defined during an API method callback if there was an error
  * @typedef {{
  *   message: (string|undefined)
@@ -250,10 +278,13 @@
  * version is very far out of date and you'd like to prompt a user to update.
  * Most other uses of requestUpdateCheck, such as calling it unconditionally
  * based on a repeating timer, probably only serve to waste client, network, and
- * server resources.</p>
- * @param {function(!chrome.runtime.RequestUpdateCheckStatus, ({
- *   version: string
- * }|undefined)): void} callback
+ * server resources.</p><p>Note: When called with a callback, instead of
+ * returning an object this function will return the two properties as separate
+ * arguments passed to the callback.</p>
+ * @param {function({
+ *   status: !chrome.runtime.RequestUpdateCheckStatus,
+ *   version: (string|undefined)
+ * }): void} callback
  * @see https://developer.chrome.com/extensions/runtime#method-requestUpdateCheck
  */
 chrome.runtime.requestUpdateCheck = function(callback) {};
@@ -280,13 +311,12 @@
 chrome.runtime.restartAfterDelay = function(seconds, callback) {};
 
 /**
- * Attempts to connect listeners within an extension/app (such as the
- * background page), or other extensions/apps. This is useful for content
- * scripts connecting to their extension processes, inter-app/extension
- * communication, and <a href="manifest/externally_connectable.html">web
- * messaging</a>. Note that this does not connect to any listeners in a content
- * script. Extensions may connect to content scripts embedded in tabs via
- * $(ref:tabs.connect).
+ * Attempts to connect listeners within an extension/app (such as the background
+ * page), or other extensions/apps. This is useful for content scripts
+ * connecting to their extension processes, inter-app/extension communication,
+ * and <a href="manifest/externally_connectable.html">web messaging</a>. Note
+ * that this does not connect to any listeners in a content script. Extensions
+ * may connect to content scripts embedded in tabs via $(ref:tabs.connect).
  * @param {string=} extensionId The ID of the extension or app to connect to. If
  *     omitted, a connection will be attempted with your own extension. Required
  *     if sending messages from a web page for <a
@@ -362,6 +392,14 @@
 chrome.runtime.getPackageDirectoryEntry = function(callback) {};
 
 /**
+ * Fetches information about active contexts associated with this extension
+ * @param {function(!Array<!chrome.runtime.ExtensionContext>): void} callback
+ *     Invoked with the matching contexts, if any.
+ * @see https://developer.chrome.com/extensions/runtime#method-getContexts
+ */
+chrome.runtime.getContexts = function(callback) {};
+
+/**
  * Fired when a profile that has this extension installed first starts up. This
  * event is not fired when an incognito profile is started, even if this
  * extension is operating in 'split' incognito mode.
diff --git a/third_party/libxml/README.chromium b/third_party/libxml/README.chromium
index 8d0700c..866d5419 100644
--- a/third_party/libxml/README.chromium
+++ b/third_party/libxml/README.chromium
@@ -1,6 +1,6 @@
 Name: libxml
 URL: http://xmlsoft.org
-Version: 44ecefc8cc299a66ac21ffec141eb261e92638da
+Version: 1061537efdf3874c91fd50d18f98c4b8a3518e52
 CPEPrefix: cpe:/a:xmlsoft:libxml2:2.11.0
 License: MIT
 License File: src/Copyright
diff --git a/third_party/libxml/src/HTMLparser.c b/third_party/libxml/src/HTMLparser.c
index 81bd11f..abcdfe2 100644
--- a/third_party/libxml/src/HTMLparser.c
+++ b/third_party/libxml/src/HTMLparser.c
@@ -411,7 +411,7 @@
 	return(ctxt->token);
     }
 
-    if ((ctxt->input->end - ctxt->input->cur < 4) &&
+    if ((ctxt->input->end - ctxt->input->cur < INPUT_CHUNK) &&
         (xmlParserGrow(ctxt) < 0))
         return(0);
 
@@ -3010,9 +3010,9 @@
         htmlParseErr(ctxt, XML_ERR_LITERAL_NOT_FINISHED,
                      "Unfinished SystemLiteral\n", NULL, NULL);
     } else {
-        NEXT;
         if (err == 0)
             ret = xmlStrndup((BASE_PTR+startPosition), len);
+        NEXT;
     }
 
     return(ret);
@@ -3065,9 +3065,9 @@
         htmlParseErr(ctxt, XML_ERR_LITERAL_NOT_FINISHED,
                      "Unfinished PubidLiteral\n", NULL, NULL);
     } else {
-        NEXT;
         if (err == 0)
             ret = xmlStrndup((BASE_PTR + startPosition), len);
+        NEXT;
     }
 
     return(ret);
@@ -3100,7 +3100,6 @@
     int nbchar = 0;
     int cur,l;
 
-    SHRINK;
     cur = CUR_CHAR(l);
     while (cur != 0) {
 	if ((cur == '<') && (NXT(1) == '/')) {
@@ -3358,7 +3357,6 @@
 	 * this is a Processing Instruction.
 	 */
 	SKIP(2);
-	SHRINK;
 
 	/*
 	 * Parse the target name and check for special support like
@@ -3481,7 +3479,6 @@
 
     state = ctxt->instate;
     ctxt->instate = XML_PARSER_COMMENT;
-    SHRINK;
     SKIP(4);
     buf = (xmlChar *) xmlMallocAtomic(size);
     if (buf == NULL) {
@@ -4477,8 +4474,8 @@
             htmlParseCharData(ctxt);
         }
 
-        GROW;
         SHRINK;
+        GROW;
     }
     if (currentNode != NULL) xmlFree(currentNode);
 }
@@ -4920,8 +4917,8 @@
             htmlParseCharData(ctxt);
         }
 
-        GROW;
         SHRINK;
+        GROW;
     }
     if (currentNode != NULL) xmlFree(currentNode);
 }
diff --git a/third_party/libxml/src/encoding.c b/third_party/libxml/src/encoding.c
index 101eba9..7eaaf3e 100644
--- a/third_party/libxml/src/encoding.c
+++ b/third_party/libxml/src/encoding.c
@@ -197,7 +197,7 @@
 	} else {
 	    *outlen = out - outstart;
 	    *inlen = processed - base;
-	    return(-1);
+	    return(-2);
 	}
 
 	processed = (const unsigned char*) in;
@@ -2037,7 +2037,7 @@
  *     as the return value is 0, else unpredictable.
  * The value of @outlen after return is the number of octets produced.
  */
-static int
+int
 xmlEncInputChunk(xmlCharEncodingHandler *handler, unsigned char *out,
                  int *outlen, const unsigned char *in, int *inlen, int flush) {
     int ret;
@@ -2123,189 +2123,12 @@
  * @out:  an xmlBuffer for the output.
  * @in:  an xmlBuffer for the input
  *
- * Front-end for the encoding handler input function, but handle only
- * the very first line, i.e. limit itself to 45 chars.
- *
- * Returns the number of byte written if success, or
- *     -1 general error
- *     -2 if the transcoding fails (for *in is not valid utf8 string or
- *        the result of transformation can't fit into the encoding we want), or
+ * DEPERECATED: Don't use.
  */
 int
 xmlCharEncFirstLine(xmlCharEncodingHandler *handler, xmlBufferPtr out,
                     xmlBufferPtr in) {
-    int ret;
-    int written;
-    int toconv;
-
-    if (handler == NULL) return(-1);
-    if (out == NULL) return(-1);
-    if (in == NULL) return(-1);
-
-    /* calculate space available */
-    written = out->size - out->use - 1; /* count '\0' */
-    toconv = in->use;
-    /*
-     * echo '<?xml version="1.0" encoding="UCS4"?>' | wc -c => 38
-     * 45 chars should be sufficient to reach the end of the encoding
-     * declaration without going too far inside the document content.
-     * on UTF-16 this means 90bytes, on UCS4 this means 180
-     * The actual value depending on guessed encoding is passed as @len
-     * if provided
-     */
-    if (toconv > 180)
-        toconv = 180;
-    if (toconv * 2 >= written) {
-        xmlBufferGrow(out, toconv * 2);
-	written = out->size - out->use - 1;
-    }
-
-    ret = xmlEncInputChunk(handler, &out->content[out->use], &written,
-                           in->content, &toconv, 0);
-    xmlBufferShrink(in, toconv);
-    out->use += written;
-    out->content[out->use] = 0;
-    if (ret == -1) ret = -3;
-
-#ifdef DEBUG_ENCODING
-    switch (ret) {
-        case 0:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "converted %d bytes to %d bytes of input\n",
-	            toconv, written);
-	    break;
-        case -1:
-	    xmlGenericError(xmlGenericErrorContext,"converted %d bytes to %d bytes of input, %d left\n",
-	            toconv, written, in->use);
-	    break;
-        case -2:
-	    xmlGenericError(xmlGenericErrorContext,
-		    "input conversion failed due to input error\n");
-	    break;
-        case -3:
-	    xmlGenericError(xmlGenericErrorContext,"converted %d bytes to %d bytes of input, %d left\n",
-	            toconv, written, in->use);
-	    break;
-	default:
-	    xmlGenericError(xmlGenericErrorContext,"Unknown input conversion failed %d\n", ret);
-    }
-#endif /* DEBUG_ENCODING */
-    /*
-     * Ignore when input buffer is not on a boundary
-     */
-    if (ret == -3) ret = 0;
-    if (ret == -1) ret = 0;
-    return(written ? written : ret);
-}
-
-/**
- * xmlCharEncFirstLineInput:
- * @input: a parser input buffer
- * @len:  number of bytes to convert for the first line, or -1
- *
- * Front-end for the encoding handler input function, but handle only
- * the very first line. Point is that this is based on autodetection
- * of the encoding and once that first line is converted we may find
- * out that a different decoder is needed to process the input.
- *
- * Returns the number of byte written if success, or
- *     -1 general error
- *     -2 if the transcoding fails (for *in is not valid utf8 string or
- *        the result of transformation can't fit into the encoding we want), or
- */
-int
-xmlCharEncFirstLineInput(xmlParserInputBufferPtr input, int len)
-{
-    int ret;
-    size_t written;
-    size_t toconv;
-    int c_in;
-    int c_out;
-    xmlBufPtr in;
-    xmlBufPtr out;
-
-    if ((input == NULL) || (input->encoder == NULL) ||
-        (input->buffer == NULL) || (input->raw == NULL))
-        return (-1);
-    out = input->buffer;
-    in = input->raw;
-
-    toconv = xmlBufUse(in);
-    if (toconv == 0)
-        return (0);
-    written = xmlBufAvail(out);
-    /*
-     * echo '<?xml version="1.0" encoding="UCS4"?>' | wc -c => 38
-     * 45 chars should be sufficient to reach the end of the encoding
-     * declaration without going too far inside the document content.
-     * on UTF-16 this means 90bytes, on UCS4 this means 180
-     * The actual value depending on guessed encoding is passed as @len
-     * if provided
-     */
-    if (len >= 0) {
-        if (toconv > (unsigned int) len)
-            toconv = len;
-    } else {
-        if (toconv > 180)
-            toconv = 180;
-    }
-    if (toconv * 2 >= written) {
-        xmlBufGrow(out, toconv * 2);
-        written = xmlBufAvail(out);
-    }
-    if (written > 360)
-        written = 360;
-
-    c_in = toconv;
-    c_out = written;
-    ret = xmlEncInputChunk(input->encoder, xmlBufEnd(out), &c_out,
-                           xmlBufContent(in), &c_in, 0);
-    xmlBufShrink(in, c_in);
-    xmlBufAddLen(out, c_out);
-    if (ret == -1)
-        ret = -3;
-
-    switch (ret) {
-        case 0:
-#ifdef DEBUG_ENCODING
-            xmlGenericError(xmlGenericErrorContext,
-                            "converted %d bytes to %d bytes of input\n",
-                            c_in, c_out);
-#endif
-            break;
-        case -1:
-#ifdef DEBUG_ENCODING
-            xmlGenericError(xmlGenericErrorContext,
-                         "converted %d bytes to %d bytes of input, %d left\n",
-                            c_in, c_out, (int)xmlBufUse(in));
-#endif
-            break;
-        case -3:
-#ifdef DEBUG_ENCODING
-            xmlGenericError(xmlGenericErrorContext,
-                        "converted %d bytes to %d bytes of input, %d left\n",
-                            c_in, c_out, (int)xmlBufUse(in));
-#endif
-            break;
-        case -2: {
-            char buf[50];
-            const xmlChar *content = xmlBufContent(in);
-
-	    snprintf(&buf[0], 49, "0x%02X 0x%02X 0x%02X 0x%02X",
-		     content[0], content[1],
-		     content[2], content[3]);
-	    buf[49] = 0;
-	    xmlEncodingErr(XML_I18N_CONV_FAILED,
-		    "input conversion failed due to input error, bytes %s\n",
-		           buf);
-        }
-    }
-    /*
-     * Ignore when input buffer is not on a boundary
-     */
-    if (ret == -3) ret = 0;
-    if (ret == -1) ret = 0;
-    return(c_out ? c_out : ret);
+    return(xmlCharEncInFunc(handler, out, in));
 }
 
 /**
diff --git a/third_party/libxml/src/include/libxml/encoding.h b/third_party/libxml/src/include/libxml/encoding.h
index 231b0be1..67add3b 100644
--- a/third_party/libxml/src/include/libxml/encoding.h
+++ b/third_party/libxml/src/include/libxml/encoding.h
@@ -203,6 +203,7 @@
 	xmlCharEncInFunc		(xmlCharEncodingHandler *handler,
 					 xmlBufferPtr out,
 					 xmlBufferPtr in);
+XML_DEPRECATED
 XMLPUBFUN int
 	xmlCharEncFirstLine		(xmlCharEncodingHandler *handler,
 					 xmlBufferPtr out,
diff --git a/third_party/libxml/src/include/private/enc.h b/third_party/libxml/src/include/private/enc.h
index ddfc8aea..cbdc2b33 100644
--- a/third_party/libxml/src/include/private/enc.h
+++ b/third_party/libxml/src/include/private/enc.h
@@ -8,7 +8,8 @@
 xmlInitEncodingInternal(void);
 
 XML_HIDDEN int
-xmlCharEncFirstLineInput(xmlParserInputBufferPtr input, int len);
+xmlEncInputChunk(xmlCharEncodingHandler *handler, unsigned char *out,
+                 int *outlen, const unsigned char *in, int *inlen, int flush);
 XML_HIDDEN int
 xmlCharEncInput(xmlParserInputBufferPtr input, int flush);
 XML_HIDDEN int
diff --git a/third_party/libxml/src/include/private/parser.h b/third_party/libxml/src/include/private/parser.h
index 18036db..820bb587 100644
--- a/third_party/libxml/src/include/private/parser.h
+++ b/third_party/libxml/src/include/private/parser.h
@@ -27,7 +27,7 @@
 xmlHaltParser(xmlParserCtxtPtr ctxt);
 XML_HIDDEN int
 xmlParserGrow(xmlParserCtxtPtr ctxt);
-XML_HIDDEN int
+XML_HIDDEN void
 xmlParserShrink(xmlParserCtxtPtr ctxt);
 
 #endif /* XML_PARSER_H_PRIVATE__ */
diff --git a/third_party/libxml/src/parser.c b/third_party/libxml/src/parser.c
index 8e548cd..3a6069c8 100644
--- a/third_party/libxml/src/parser.c
+++ b/third_party/libxml/src/parser.c
@@ -4183,7 +4183,6 @@
     xmlChar stop;
     int state = ctxt->instate;
 
-    SHRINK;
     if (RAW == '"') {
         NEXT;
 	stop = '"';
@@ -4265,7 +4264,6 @@
     xmlChar stop;
     xmlParserInputState oldstate = ctxt->instate;
 
-    SHRINK;
     if (RAW == '"') {
         NEXT;
 	stop = '"';
@@ -4387,7 +4385,6 @@
     int col = ctxt->input->col;
     int ccol;
 
-    SHRINK;
     GROW;
     /*
      * Accelerated common case where input don't need to be
@@ -4533,7 +4530,6 @@
     int nbchar = 0;
     int cur, l;
 
-    SHRINK;
     cur = CUR_CHAR(l);
     while ((cur != '<') && /* checked */
            (cur != '&') &&
@@ -4629,8 +4625,6 @@
 xmlParseExternalID(xmlParserCtxtPtr ctxt, xmlChar **publicID, int strict) {
     xmlChar *URI = NULL;
 
-    SHRINK;
-
     *publicID = NULL;
     if (CMP6(CUR_PTR, 'S', 'Y', 'S', 'T', 'E', 'M')) {
         SKIP(6);
@@ -4847,7 +4841,6 @@
     ctxt->instate = XML_PARSER_COMMENT;
     inputid = ctxt->input->id;
     SKIP(2);
-    SHRINK;
     GROW;
 
     /*
@@ -5133,7 +5126,6 @@
 	 * this is a Processing Instruction.
 	 */
 	SKIP(2);
-	SHRINK;
 
 	/*
 	 * Parse the target name and check for special support like
@@ -5272,7 +5264,6 @@
 
     if (CMP8(CUR_PTR, 'N', 'O', 'T', 'A', 'T', 'I', 'O', 'N')) {
 	int inputid = ctxt->input->id;
-	SHRINK;
 	SKIP(8);
 	if (SKIP_BLANKS == 0) {
 	    xmlFatalErrMsg(ctxt, XML_ERR_SPACE_REQUIRED,
@@ -5360,7 +5351,6 @@
     /* GROW; done in the caller */
     if (CMP6(CUR_PTR, 'E', 'N', 'T', 'I', 'T', 'Y')) {
 	int inputid = ctxt->input->id;
-	SHRINK;
 	SKIP(6);
 	if (SKIP_BLANKS == 0) {
 	    xmlFatalErrMsg(ctxt, XML_ERR_SPACE_REQUIRED,
@@ -5684,7 +5674,6 @@
 	xmlFatalErr(ctxt, XML_ERR_NOTATION_NOT_STARTED, NULL);
 	return(NULL);
     }
-    SHRINK;
     do {
         NEXT;
 	SKIP_BLANKS;
@@ -5756,7 +5745,6 @@
 	xmlFatalErr(ctxt, XML_ERR_ATTLIST_NOT_STARTED, NULL);
 	return(NULL);
     }
-    SHRINK;
     do {
         NEXT;
 	SKIP_BLANKS;
@@ -5885,7 +5873,6 @@
  */
 int
 xmlParseAttributeType(xmlParserCtxtPtr ctxt, xmlEnumerationPtr *tree) {
-    SHRINK;
     if (CMP5(CUR_PTR, 'C', 'D', 'A', 'T', 'A')) {
 	SKIP(5);
 	return(XML_ATTRIBUTE_CDATA);
@@ -6070,7 +6057,6 @@
     if (CMP7(CUR_PTR, '#', 'P', 'C', 'D', 'A', 'T', 'A')) {
 	SKIP(7);
 	SKIP_BLANKS;
-	SHRINK;
 	if (RAW == ')') {
 	    if (ctxt->input->id != inputchk) {
 		xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY,
@@ -6242,7 +6228,6 @@
 	GROW;
     }
     SKIP_BLANKS;
-    SHRINK;
     while ((RAW != ')') && (ctxt->instate != XML_PARSER_EOF)) {
         /*
 	 * Each loop we parse one separator and one element.
@@ -6787,6 +6772,7 @@
             break;
 
         SKIP_BLANKS;
+        SHRINK;
         GROW;
     }
 
@@ -7018,6 +7004,7 @@
             return;
         }
         SKIP_BLANKS;
+        SHRINK;
     }
 
     if (RAW != 0) {
@@ -8343,6 +8330,8 @@
                 return;
             }
 	    SKIP_BLANKS;
+            SHRINK;
+            GROW;
 	}
 	if (RAW == ']') {
 	    NEXT;
@@ -9229,14 +9218,6 @@
     if (RAW != '<') return(NULL);
     NEXT1;
 
-    /*
-     * NOTE: it is crucial with the SAX2 API to never call SHRINK beyond that
-     *       point since the attribute values may be stored as pointers to
-     *       the buffer and calling SHRINK would destroy them !
-     *       The Shrinking is only possible once the full set of attribute
-     *       callbacks have been done.
-     */
-    SHRINK;
     cur = ctxt->input->cur - ctxt->input->base;
     inputid = ctxt->input->id;
     nbatts = 0;
@@ -9881,8 +9862,8 @@
 	    xmlParseCharData(ctxt, 0);
 	}
 
-	GROW;
 	SHRINK;
+	GROW;
     }
 }
 
@@ -10248,6 +10229,9 @@
     xmlChar *buf = NULL;
     int len = 0;
     int size = 10;
+    int maxLength = (ctxt->options & XML_PARSE_HUGE) ?
+                    XML_MAX_TEXT_LENGTH :
+                    XML_MAX_NAME_LENGTH;
     xmlChar cur;
 
     cur = CUR;
@@ -10280,13 +10264,13 @@
 		buf = tmp;
 	    }
 	    buf[len++] = cur;
+            if (len > maxLength) {
+                xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "EncName");
+                xmlFree(buf);
+                return(NULL);
+            }
 	    NEXT;
 	    cur = CUR;
-	    if (cur == 0) {
-	        SHRINK;
-		GROW;
-		cur = CUR;
-	    }
         }
 	buf[len] = 0;
     } else {
@@ -10383,6 +10367,7 @@
         else if ((encoding != NULL) &&
 	    ((!xmlStrcasecmp(encoding, BAD_CAST "UTF-8")) ||
 	     (!xmlStrcasecmp(encoding, BAD_CAST "UTF8")))) {
+            /* TODO: Check for encoding mismatch. */
 	    if (ctxt->encoding != NULL)
 		xmlFree((xmlChar *) ctxt->encoding);
 	    ctxt->encoding = encoding;
@@ -10708,15 +10693,7 @@
 	return(-1);
     }
 
-    /*
-     * Check for the XMLDecl in the Prolog.
-     * do not GROW here to avoid the detected encoder to decode more
-     * than just the first line, unless the amount of data is really
-     * too small to hold "<?xml version="1.0" encoding="foo"
-     */
-    if ((ctxt->input->end - ctxt->input->cur) < 35) {
-       GROW;
-    }
+    GROW;
     if ((CMP5(CUR_PTR, '<', '?', 'x', 'm', 'l')) && (IS_BLANK_CH(NXT(5)))) {
 
 	/*
@@ -11363,12 +11340,9 @@
 	    /*
 	     * If we are operating on converted input, try to flush
 	     * remaining chars to avoid them stalling in the non-converted
-	     * buffer. But do not do this in document start where
-	     * encoding="..." may not have been read and we work on a
-	     * guessed encoding.
+	     * buffer.
 	     */
-	    if ((ctxt->instate != XML_PARSER_START) &&
-	        (ctxt->input->buf->raw != NULL) &&
+	    if ((ctxt->input->buf->raw != NULL) &&
 		(xmlBufIsEmpty(ctxt->input->buf->raw) == 0)) {
                 size_t base = xmlBufGetInputBase(ctxt->input->buf->buffer,
                                                  ctxt->input);
@@ -11411,6 +11385,13 @@
 		    start[2] = NXT(2);
 		    start[3] = NXT(3);
 		    enc = xmlDetectCharEncoding(start, 4);
+                    /*
+                     * We need more bytes to detect EBCDIC code pages.
+                     * See xmlDetectEBCDIC.
+                     */
+                    if ((enc == XML_CHAR_ENCODING_EBCDIC) &&
+                        (!terminate) && (avail < 200))
+                        goto done;
 		    xmlSwitchEncoding(ctxt, enc);
 		    break;
 		}
@@ -12202,15 +12183,8 @@
     xmlParserCtxtPtr ctxt;
     xmlParserInputPtr inputStream;
     xmlParserInputBufferPtr buf;
-    xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
 
-    /*
-     * plug some encoding conversion routines
-     */
-    if ((chunk != NULL) && (size >= 4))
-	enc = xmlDetectCharEncoding((const xmlChar *) chunk, size);
-
-    buf = xmlAllocParserInputBuffer(enc);
+    buf = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE);
     if (buf == NULL) return(NULL);
 
     ctxt = xmlNewSAXParserCtxt(sax, user_data);
@@ -12269,10 +12243,6 @@
 #endif
     }
 
-    if (enc != XML_CHAR_ENCODING_NONE) {
-        xmlSwitchEncoding(ctxt, enc);
-    }
-
     return(ctxt);
 }
 #endif /* LIBXML_PUSH_ENABLED */
diff --git a/third_party/libxml/src/parserInternals.c b/third_party/libxml/src/parserInternals.c
index dd16579..8dc6005 100644
--- a/third_party/libxml/src/parserInternals.c
+++ b/third_party/libxml/src/parserInternals.c
@@ -409,17 +409,16 @@
  * xmlParserShrink:
  * @ctxt:  an XML parser context
  */
-int
+void
 xmlParserShrink(xmlParserCtxtPtr ctxt) {
     xmlParserInputPtr in = ctxt->input;
     xmlParserInputBufferPtr buf = in->buf;
     size_t used;
-    int ret = 0;
 
     /* Don't shrink memory buffers. */
     if ((buf == NULL) ||
         ((buf->encoder == NULL) && (buf->readcallback == NULL)))
-        return(0);
+        return;
 
     used = in->cur - in->base;
     /*
@@ -439,18 +438,7 @@
 	}
     }
 
-    if (xmlBufUse(buf->buffer) < INPUT_CHUNK)
-        ret = xmlParserInputBufferGrow(buf, INPUT_CHUNK);
-
     xmlBufSetInputBaseCur(buf->buffer, in, 0, used);
-
-    /* TODO: Get error code from xmlParserInputBufferGrow */
-    if (ret < 0) {
-        xmlErrInternal(ctxt, "Growing input buffer", NULL);
-        xmlHaltParser(ctxt);
-    }
-
-    return(ret);
 }
 
 /**
@@ -539,7 +527,7 @@
 	return;
     }
 
-    if (ctxt->input->end - ctxt->input->cur < 4) {
+    if (ctxt->input->end - ctxt->input->cur < INPUT_CHUNK) {
         if (xmlParserGrow(ctxt) < 0)
             return;
         if (ctxt->input->cur >= ctxt->input->end)
@@ -686,7 +674,7 @@
     if (ctxt->instate == XML_PARSER_EOF)
 	return(0);
 
-    if ((ctxt->input->end - ctxt->input->cur < 4) &&
+    if ((ctxt->input->end - ctxt->input->cur < INPUT_CHUNK) &&
         (xmlParserGrow(ctxt) < 0))
         return(0);
 
@@ -1002,9 +990,63 @@
  *									*
  ************************************************************************/
 
-static int
-xmlSwitchInputEncodingInt(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
-                          xmlCharEncodingHandlerPtr handler, int len);
+static xmlCharEncodingHandlerPtr
+xmlDetectEBCDIC(xmlParserInputPtr input) {
+    xmlChar out[200];
+    xmlCharEncodingHandlerPtr handler;
+    int inlen, outlen, res, i;
+
+    /*
+     * To detect the EBCDIC code page, we convert the first 200 bytes
+     * to EBCDIC-US and try to find the encoding declaration.
+     */
+    handler = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_EBCDIC);
+    if (handler == NULL)
+        return(NULL);
+    outlen = sizeof(out) - 1;
+    inlen = input->end - input->cur;
+    res = xmlEncInputChunk(handler, out, &outlen, input->cur, &inlen, 0);
+    if (res < 0)
+        return(handler);
+    out[outlen] = 0;
+
+    for (i = 0; i < outlen; i++) {
+        if (out[i] == '>')
+            break;
+        if ((out[i] == 'e') &&
+            (xmlStrncmp(out + i, BAD_CAST "encoding", 8) == 0)) {
+            int start, cur, quote;
+
+            i += 8;
+            while (IS_BLANK_CH(out[i]))
+                i += 1;
+            if (out[i++] != '=')
+                break;
+            while (IS_BLANK_CH(out[i]))
+                i += 1;
+            quote = out[i++];
+            if ((quote != '\'') && (quote != '"'))
+                break;
+            start = i;
+            cur = out[i];
+            while (((cur >= 'a') && (cur <= 'z')) ||
+                   ((cur >= 'A') && (cur <= 'Z')) ||
+                   ((cur >= '0') && (cur <= '9')) ||
+                   (cur == '.') || (cur == '_') ||
+                   (cur == '-'))
+                cur = out[++i];
+            if (cur != quote)
+                break;
+            out[i] = 0;
+            xmlCharEncCloseFunc(handler);
+            handler = xmlFindCharEncodingHandler((char *) out + start);
+            break;
+        }
+    }
+
+    return(handler);
+}
+
 /**
  * xmlSwitchEncoding:
  * @ctxt:  the parser context
@@ -1019,7 +1061,6 @@
 xmlSwitchEncoding(xmlParserCtxtPtr ctxt, xmlCharEncoding enc)
 {
     xmlCharEncodingHandlerPtr handler;
-    int len = -1;
     int ret;
 
     if (ctxt == NULL) return(-1);
@@ -1048,51 +1089,13 @@
 		ctxt->input->cur += 3;
 	    }
 	    return(0);
-    case XML_CHAR_ENCODING_UTF16LE:
-    case XML_CHAR_ENCODING_UTF16BE:
-        /*The raw input characters are encoded
-         *in UTF-16. As we expect this function
-         *to be called after xmlCharEncInFunc, we expect
-         *ctxt->input->cur to contain UTF-8 encoded characters.
-         *So the raw UTF16 Byte Order Mark
-         *has also been converted into
-         *an UTF-8 BOM. Let's skip that BOM.
-         */
-        if ((ctxt->input != NULL) && (ctxt->input->cur != NULL) &&
-            (ctxt->input->cur[0] == 0xEF) &&
-            (ctxt->input->cur[1] == 0xBB) &&
-            (ctxt->input->cur[2] == 0xBF)) {
-            ctxt->input->cur += 3;
-        }
-        len = 90;
-	break;
-    case XML_CHAR_ENCODING_UCS2:
-        len = 90;
-	break;
-    case XML_CHAR_ENCODING_UCS4BE:
-    case XML_CHAR_ENCODING_UCS4LE:
-    case XML_CHAR_ENCODING_UCS4_2143:
-    case XML_CHAR_ENCODING_UCS4_3412:
-        len = 180;
-	break;
-    case XML_CHAR_ENCODING_EBCDIC:
-    case XML_CHAR_ENCODING_8859_1:
-    case XML_CHAR_ENCODING_8859_2:
-    case XML_CHAR_ENCODING_8859_3:
-    case XML_CHAR_ENCODING_8859_4:
-    case XML_CHAR_ENCODING_8859_5:
-    case XML_CHAR_ENCODING_8859_6:
-    case XML_CHAR_ENCODING_8859_7:
-    case XML_CHAR_ENCODING_8859_8:
-    case XML_CHAR_ENCODING_8859_9:
-    case XML_CHAR_ENCODING_ASCII:
-    case XML_CHAR_ENCODING_2022_JP:
-    case XML_CHAR_ENCODING_SHIFT_JIS:
-    case XML_CHAR_ENCODING_EUC_JP:
-        len = 45;
-	break;
+        case XML_CHAR_ENCODING_EBCDIC:
+            handler = xmlDetectEBCDIC(ctxt->input);
+            break;
+        default:
+            handler = xmlGetCharEncodingHandler(enc);
+            break;
     }
-    handler = xmlGetCharEncodingHandler(enc);
     if (handler == NULL) {
 	/*
 	 * Default handlers.
@@ -1124,7 +1127,7 @@
                 return(-1);
         }
     }
-    ret = xmlSwitchInputEncodingInt(ctxt, ctxt->input, handler, len);
+    ret = xmlSwitchInputEncoding(ctxt, ctxt->input, handler);
     if ((ret < 0) || (ctxt->errNo == XML_I18N_CONV_FAILED)) {
         /*
 	 * on encoding conversion errors, stop the parser
@@ -1136,20 +1139,19 @@
 }
 
 /**
- * xmlSwitchInputEncodingInt:
+ * xmlSwitchInputEncoding:
  * @ctxt:  the parser context
  * @input:  the input stream
  * @handler:  the encoding handler
- * @len:  the number of bytes to convert for the first line or -1
  *
  * change the input functions when discovering the character encoding
  * of a given entity.
  *
  * Returns 0 in case of success, -1 otherwise
  */
-static int
-xmlSwitchInputEncodingInt(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
-                          xmlCharEncodingHandlerPtr handler, int len)
+int
+xmlSwitchInputEncoding(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
+                       xmlCharEncodingHandlerPtr handler)
 {
     int nbchars;
     xmlParserInputBufferPtr in;
@@ -1171,30 +1173,17 @@
 	return (-1);
     }
 
-    ctxt->charset = XML_CHAR_ENCODING_UTF8;
-
     if (in->encoder != NULL) {
         /*
-         * Check in case the auto encoding detection triggered
-         * in already.
+         * TODO: Detect encoding mismatch. We should start by comparing
+         * in->encoder->name and handler->name, but there are a few
+         * compatible encodings like UTF-16 and UCS-2 or UTF-32 and UCS-4.
          */
-        if (in->encoder == handler)
-            return (0);
-
-        /*
-         * Note: this is a bit dangerous, but that's what it
-         * takes to use nearly compatible signature for different
-         * encodings.
-         *
-         * FIXME: Encoders might buffer partial byte sequences, so
-         * this probably can't work. We should return an error and
-         * make sure that callers never try to switch the encoding
-         * twice.
-         */
-        xmlCharEncCloseFunc(in->encoder);
-        in->encoder = handler;
+        xmlCharEncCloseFunc(handler);
         return (0);
     }
+
+    ctxt->charset = XML_CHAR_ENCODING_UTF8;
     in->encoder = handler;
 
     /*
@@ -1242,20 +1231,7 @@
         in->rawconsumed = processed;
         use = xmlBufUse(in->raw);
 
-        if (ctxt->html) {
-            /*
-             * convert as much as possible of the buffer
-             */
-            nbchars = xmlCharEncInput(in, 1);
-        } else {
-            /*
-             * convert just enough to get
-             * '<?xml version="1.0" encoding="xxx"?>'
-             * parsed with the autodetected encoding
-             * into the parser reading buffer.
-             */
-            nbchars = xmlCharEncFirstLineInput(in, len);
-        }
+        nbchars = xmlCharEncInput(in, 0);
         xmlBufResetInput(in->buffer, input);
         if (nbchars < 0) {
             xmlErrInternal(ctxt,
@@ -1274,25 +1250,6 @@
 }
 
 /**
- * xmlSwitchInputEncoding:
- * @ctxt:  the parser context
- * @input:  the input stream
- * @handler:  the encoding handler
- *
- * DEPRECATED: Use xmlSwitchToEncoding
- *
- * change the input functions when discovering the character encoding
- * of a given entity.
- *
- * Returns 0 in case of success, -1 otherwise
- */
-int
-xmlSwitchInputEncoding(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
-                          xmlCharEncodingHandlerPtr handler) {
-    return(xmlSwitchInputEncodingInt(ctxt, input, handler, -1));
-}
-
-/**
  * xmlSwitchToEncoding:
  * @ctxt:  the parser context
  * @handler:  the encoding handler
@@ -1307,7 +1264,7 @@
 {
     if (ctxt == NULL)
         return(-1);
-    return(xmlSwitchInputEncodingInt(ctxt, ctxt->input, handler, -1));
+    return(xmlSwitchInputEncoding(ctxt, ctxt->input, handler));
 }
 
 /************************************************************************
diff --git a/third_party/libxslt/README.chromium b/third_party/libxslt/README.chromium
index 1c7d2bcc..a225d68 100644
--- a/third_party/libxslt/README.chromium
+++ b/third_party/libxslt/README.chromium
@@ -1,6 +1,6 @@
 Name: libxslt
 URL: http://xmlsoft.org/XSLT
-Version: 0203b5c5fa9c2d8197e0df7b6fd131be855ef173
+Version: f80ae929fa9e80d66d4c42108c6fb2456ce14b8b
 CPEPrefix: cpe:/a:xmlsoft:libxslt:1.1.37
 Security Critical: yes
 License: MIT
diff --git a/third_party/libxslt/chromium/xslt-locale.patch b/third_party/libxslt/chromium/xslt-locale.patch
index 88398ce..4e8ef110 100644
--- a/third_party/libxslt/chromium/xslt-locale.patch
+++ b/third_party/libxslt/chromium/xslt-locale.patch
@@ -5,39 +5,41 @@
 POSIX and Windows locale support, we preserve the behavior WebKit expects in
 XsltUnicodeSortFunction.
 
-diff --git a/src/libxslt/xsltlocale.h b/src/libxslt/xsltlocale.h
---- src/libxslt/xsltlocale.h
-+++ src/libxslt/xsltlocale.h
-@@ -14,7 +14,12 @@
- #include <libxml/xmlstring.h>
- #include "xsltexports.h"
+In addition, it would seem that strxfrm_l is not supported on Fuchsia as using
+it without this patch causes a compile error.
+
+diff --git a/libxslt/xsltlocale.c b/libxslt/xsltlocale.c
+index 5a929188..9324f284 100644
+--- a/libxslt/xsltlocale.c
++++ b/libxslt/xsltlocale.c
+@@ -19,29 +19,7 @@
+ #include "xsltlocale.h"
+ #include "xsltutils.h"
  
 -#ifdef HAVE_STRXFRM_L
-+#if defined(_WIN32) && !defined(__CYGWIN__)
-+#include <windows.h>
-+#include <winnls.h>
-+#endif
-+
-+#if 0
- 
- /*
-  * XSLT_LOCALE_POSIX:
-@@ -32,7 +37,7 @@
- typedef locale_t xsltLocale;
- typedef xmlChar xsltLocaleChar;
- 
--#elif defined(_WIN32)
-+#elif 0
- 
- /*
-  * XSLT_LOCALE_WINAPI:
-@@ -40,9 +45,6 @@
-  */
- #define XSLT_LOCALE_WINAPI
- 
--#include <windows.h>
--#include <winnls.h>
 -
- typedef LCID xsltLocale;
- typedef wchar_t xsltLocaleChar;
+-  #define XSLT_LOCALE_POSIX
+-
+-  #ifdef HAVE_LOCALE_H
+-    #include <locale.h>
+-  #endif
+-  #ifdef HAVE_XLOCALE_H
+-    #include <xlocale.h>
+-  #endif
+-
+-#elif defined(_WIN32)
+-
+-  #define XSLT_LOCALE_WINAPI
+-
+-  #include <windows.h>
+-  #include <winnls.h>
+-
+-#else
+-
+-  #define XSLT_LOCALE_NONE
+-
+-#endif
++#define XSLT_LOCALE_NONE
  
+ #define TOUPPER(c) (c & ~0x20)
+ #define TOLOWER(c) (c | 0x20)
diff --git a/third_party/libxslt/src/README b/third_party/libxslt/src/README
deleted file mode 100644
index 94402947..0000000
--- a/third_party/libxslt/src/README
+++ /dev/null
@@ -1,22 +0,0 @@
-
-     XSLT support for libxml2 (XML toolkit from the GNOME project)
-
-Full documentation is available on-line at
-    https://gitlab.gnome.org/GNOME/libxslt/-/wikis
-
-This code is released under the MIT Licence see the Copyright file.
- 
-To report bugs, follow the instructions at:
-    https://gitlab.gnome.org/GNOME/libxslt/-/issues
-
-A mailing-list xslt@gnome.org is available, to subscribe:
-    http://mail.gnome.org/mailman/listinfo/xslt
-
-The list archive is at:
-    http://mail.gnome.org/archives/xslt/
-
-All technical answers asked privately will be automatically answered on
-the list and archived for public access unless pricacy is explicitely
-required and justified.
-
-Daniel Veillard
diff --git a/third_party/libxslt/src/README.md b/third_party/libxslt/src/README.md
new file mode 100644
index 0000000..ff0d56f
--- /dev/null
+++ b/third_party/libxslt/src/README.md
@@ -0,0 +1,19 @@
+# libxslt
+
+libxslt is an XSLT processor based on libxml2.
+
+Official releases can be downloaded from
+<https://download.gnome.org/sources/libxslt/>
+
+The git repository is hosted on GNOME's GitLab server:
+<https://gitlab.gnome.org/GNOME/libxslt>
+
+Bugs should be reported at
+<https://gitlab.gnome.org/GNOME/libxslt/-/issues>
+
+Documentation is available at
+<https://gitlab.gnome.org/GNOME/libxslt/-/wikis>
+
+The build system is similar to libxml2. Refer to libxml2's README for
+build instructions.
+
diff --git a/third_party/libxslt/src/configure.ac b/third_party/libxslt/src/configure.ac
index 8064c7bc..540a2f2 100644
--- a/third_party/libxslt/src/configure.ac
+++ b/third_party/libxslt/src/configure.ac
@@ -564,7 +564,5 @@
 xslt-config
 libxslt.spec
 ])
-AC_CONFIG_LINKS([tests/fuzz/xpath.xml:tests/fuzz/xpath.xml])
-AC_CONFIG_LINKS([tests/fuzz/xslt.xml:tests/fuzz/xslt.xml])
 
 AC_OUTPUT
diff --git a/third_party/libxslt/src/libexslt/date.c b/third_party/libxslt/src/libexslt/date.c
index 60d353d..21214a1f 100644
--- a/third_party/libxslt/src/libexslt/date.c
+++ b/third_party/libxslt/src/libexslt/date.c
@@ -227,37 +227,39 @@
 }
 
 /**
- * FORMAT_GYEAR:
+ * exsltFormatGYear:
+ * @cur: a pointer to a pointer to an allocated buffer
+ * @end: a pointer to the end of @cur buffer
  * @yr:  the year to format
- * @cur: a pointer to an allocated buffer
  *
  * Formats @yr in xsl:gYear format. Result is appended to @cur and
  * @cur is updated to point after the xsl:gYear.
  */
-#define FORMAT_GYEAR(yr, cur)					\
-	if (yr <= 0) {					        \
-	    *cur = '-';						\
-	    cur++;						\
-	}							\
-	{							\
-	    long year = (yr <= 0) ? -yr + 1 : yr;               \
-	    xmlChar tmp_buf[100], *tmp = tmp_buf;		\
-	    /* result is in reverse-order */			\
-	    while (year > 0) {					\
-		*tmp = '0' + (xmlChar)(year % 10);		\
-		year /= 10;					\
-		tmp++;						\
-	    }							\
-	    /* virtually adds leading zeros */			\
-	    while ((tmp - tmp_buf) < 4)				\
-		*tmp++ = '0';					\
-	    /* restore the correct order */			\
-	    while (tmp > tmp_buf) {				\
-		tmp--;						\
-		*cur = *tmp;					\
-		cur++;						\
-	    }							\
-	}
+static void
+exsltFormatGYear(xmlChar **cur, xmlChar *end, long yr)
+{
+    if (yr <= 0 && *cur < end) {
+        *(*cur)++ = '-';
+    }
+
+    long year = (yr <= 0) ? -yr + 1 : yr;
+    xmlChar tmp_buf[100], *tmp = tmp_buf, *tmp_end = tmp_buf + 99;
+    /* result is in reverse-order */
+    while (year > 0 && tmp < tmp_end) {
+        *tmp++ = '0' + (xmlChar)(year % 10);
+        year /= 10;
+    }
+
+    /* virtually adds leading zeros */
+    while ((tmp - tmp_buf) < 4)
+        *tmp++ = '0';
+
+    /* restore the correct order */
+    while (tmp > tmp_buf && *cur < end) {
+        tmp--;
+        *(*cur)++ = *tmp;
+    }
+}
 
 /**
  * PARSE_2_DIGITS:
@@ -286,18 +288,22 @@
 	cur += 2;
 
 /**
- * FORMAT_2_DIGITS:
- * @num:  the integer to format
- * @cur: a pointer to an allocated buffer
+ * exsltFormat2Digits:
+ * @cur: a pointer to a pointer to an allocated buffer
+ * @end: a pointer to the end of @cur buffer
+ * @num: the integer to format
  *
  * Formats a 2-digits integer. Result is appended to @cur and
  * @cur is updated to point after the integer.
  */
-#define FORMAT_2_DIGITS(num, cur)				\
-	*cur = '0' + ((num / 10) % 10);				\
-	cur++;							\
-	*cur = '0' + (num % 10);				\
-	cur++;
+static void
+exsltFormat2Digits(xmlChar **cur, xmlChar *end, unsigned int num)
+{
+    if (*cur < end)
+        *(*cur)++ = '0' + ((num / 10) % 10);
+    if (*cur < end)
+        *(*cur)++ = '0' + (num % 10);
+}
 
 /**
  * PARSE_FLOAT:
@@ -326,29 +332,6 @@
 	}
 
 /**
- * FORMAT_FLOAT:
- * @num:  the double to format
- * @cur: a pointer to an allocated buffer
- * @pad: a flag for padding to 2 integer digits
- *
- * Formats a float. Result is appended to @cur and @cur is updated to
- * point after the integer. If the @pad flag is non-zero, then the
- * float representation has a minimum 2-digits integer part. The
- * fractional part is formatted if @num has a fractional value.
- */
-#define FORMAT_FLOAT(num, cur, pad)				\
-	{							\
-            xmlChar *sav, *str;                                 \
-            if ((pad) && (num < 10.0))                          \
-                *cur++ = '0';                                   \
-            str = xmlXPathCastNumberToString(num);              \
-            sav = str;                                          \
-            while (*str != 0)                                   \
-                *cur++ = *str++;                                \
-            xmlFree(sav);                                       \
-	}
-
-/**
  * _exsltDateParseGMonth:
  * @dt:  pointer to a date structure
  * @str: pointer to the string to analyze
@@ -380,17 +363,6 @@
 }
 
 /**
- * FORMAT_GMONTH:
- * @mon:  the month to format
- * @cur: a pointer to an allocated buffer
- *
- * Formats @mon in xsl:gMonth format. Result is appended to @cur and
- * @cur is updated to point after the xsl:gMonth.
- */
-#define FORMAT_GMONTH(mon, cur)					\
-	FORMAT_2_DIGITS(mon, cur)
-
-/**
  * _exsltDateParseGDay:
  * @dt:  pointer to a date structure
  * @str: pointer to the string to analyze
@@ -422,32 +394,25 @@
 }
 
 /**
- * FORMAT_GDAY:
+ * exsltFormatYearMonthDay:
+ * @cur: a pointer to a pointer to an allocated buffer
+ * @end: a pointer to the end of @cur buffer
  * @dt:  the #exsltDateVal to format
- * @cur: a pointer to an allocated buffer
- *
- * Formats @dt in xsl:gDay format. Result is appended to @cur and
- * @cur is updated to point after the xsl:gDay.
- */
-#define FORMAT_GDAY(dt, cur)					\
-	FORMAT_2_DIGITS(dt->day, cur)
-
-/**
- * FORMAT_DATE:
- * @dt:  the #exsltDateVal to format
- * @cur: a pointer to an allocated buffer
  *
  * Formats @dt in xsl:date format. Result is appended to @cur and
  * @cur is updated to point after the xsl:date.
  */
-#define FORMAT_DATE(dt, cur)					\
-	FORMAT_GYEAR(dt->year, cur);				\
-	*cur = '-';						\
-	cur++;							\
-	FORMAT_GMONTH(dt->mon, cur);				\
-	*cur = '-';						\
-	cur++;							\
-	FORMAT_GDAY(dt, cur);
+static void
+exsltFormatYearMonthDay(xmlChar **cur, xmlChar *end, const exsltDateValPtr dt)
+{
+    exsltFormatGYear(cur, end, dt->year);
+    if (*cur < end)
+        *(*cur)++ = '-';
+    exsltFormat2Digits(cur, end, dt->mon);
+    if (*cur < end)
+        *(*cur)++ = '-';
+    exsltFormat2Digits(cur, end, dt->day);
+}
 
 /**
  * _exsltDateParseTime:
@@ -506,23 +471,6 @@
 }
 
 /**
- * FORMAT_TIME:
- * @dt:  the #exsltDateVal to format
- * @cur: a pointer to an allocated buffer
- *
- * Formats @dt in xsl:time format. Result is appended to @cur and
- * @cur is updated to point after the xsl:time.
- */
-#define FORMAT_TIME(dt, cur)					\
-	FORMAT_2_DIGITS(dt->hour, cur);				\
-	*cur = ':';						\
-	cur++;							\
-	FORMAT_2_DIGITS(dt->min, cur);				\
-	*cur = ':';						\
-	cur++;							\
-	FORMAT_FLOAT(dt->sec, cur, 1);
-
-/**
  * _exsltDateParseTimeZone:
  * @dt:  pointer to a date structure
  * @str: pointer to the string to analyze
@@ -600,27 +548,31 @@
 }
 
 /**
- * FORMAT_TZ:
- * @tzo:  the timezone offset to format
- * @cur: a pointer to an allocated buffer
+ * exsltFormatTimeZone:
+ * @cur: a pointer to a pointer to an allocated buffer
+ * @end: a pointer to the end of @cur buffer
+ * @tzo: the timezone offset to format
  *
  * Formats @tzo timezone. Result is appended to @cur and
  * @cur is updated to point after the timezone.
  */
-#define FORMAT_TZ(tzo, cur)					\
-	if (tzo == 0) {					        \
-	    *cur = 'Z';						\
-	    cur++;						\
-	} else {						\
-	    int aTzo = (tzo < 0) ? - tzo : tzo;                 \
-	    int tzHh = aTzo / 60, tzMm = aTzo % 60;		\
-	    *cur = (tzo < 0) ? '-' : '+' ;			\
-	    cur++;						\
-	    FORMAT_2_DIGITS(tzHh, cur);				\
-	    *cur = ':';						\
-	    cur++;						\
-	    FORMAT_2_DIGITS(tzMm, cur);				\
-	}
+static void
+exsltFormatTimeZone(xmlChar **cur, xmlChar *end, int tzo)
+{
+    if (tzo == 0) {
+        if (*cur < end)
+            *(*cur)++ = 'Z';
+    } else {
+        unsigned int aTzo = (tzo < 0) ? -tzo : tzo;
+        unsigned int tzHh = aTzo / 60, tzMm = aTzo % 60;
+        if (*cur < end)
+            *(*cur)++ = (tzo < 0) ? '-' : '+';
+        exsltFormat2Digits(cur, end, tzHh);
+        if (*cur < end)
+            *(*cur)++ = ':';
+        exsltFormat2Digits(cur, end, tzMm);
+    }
+}
 
 /****************************************************************
  *								*
@@ -718,7 +670,7 @@
 exsltDateCurrent (void)
 {
     struct tm localTm, gmTm;
-#ifndef HAVE_GMTIME_R
+#if !defined(HAVE_GMTIME_R) && !defined(_WIN32)
     struct tm *tb = NULL;
 #endif
     time_t secs;
@@ -740,7 +692,11 @@
         errno = 0;
 	secs = (time_t) strtol (source_date_epoch, NULL, 10);
 	if (errno == 0) {
-#if HAVE_GMTIME_R
+#ifdef _WIN32
+	    struct tm *gm = gmtime_s(&localTm, &secs) ? NULL : &localTm;
+	    if (gm != NULL)
+	        override = 1;
+#elif HAVE_GMTIME_R
 	    if (gmtime_r(&secs, &localTm) != NULL)
 	        override = 1;
 #else
@@ -757,7 +713,9 @@
     /* get current time */
 	secs    = time(NULL);
 
-#if HAVE_LOCALTIME_R
+#ifdef _WIN32
+	localtime_s(&localTm, &secs);
+#elif HAVE_LOCALTIME_R
 	localtime_r(&secs, &localTm);
 #else
 	localTm = *localtime(&secs);
@@ -776,7 +734,9 @@
     ret->sec  = (double) localTm.tm_sec;
 
     /* determine the time zone offset from local to gm time */
-#if HAVE_GMTIME_R
+#ifdef _WIN32
+    gmtime_s(&gmTm, &secs);
+#elif HAVE_GMTIME_R
     gmtime_r(&secs, &gmTm);
 #else
     tb = gmtime(&secs);
@@ -1138,21 +1098,41 @@
     return NULL;
 }
 
-/**
- * FORMAT_ITEM:
- * @num:        number to format
- * @cur:        current location to convert number
- * @limit:      max value
- * @item:       char designator
- *
- */
-#define FORMAT_ITEM(num, cur, limit, item)			\
-        if (num >= limit) {					\
-            double comp = floor(num / limit);			\
-            FORMAT_FLOAT(comp, cur, 0);				\
-            *cur++ = item;					\
-            num -= comp * limit;				\
+static void
+exsltFormatLong(xmlChar **cur, xmlChar *end, long num) {
+    xmlChar buf[20];
+    int i = 0;
+
+    while (i < 20) {
+        buf[i++] = '0' + num % 10;
+        num /= 10;
+        if (num == 0)
+            break;
+    }
+
+    while (i > 0) {
+        if (*cur < end)
+            *(*cur)++ = buf[--i];
+    }
+}
+
+static void
+exsltFormatNanoseconds(xmlChar **cur, xmlChar *end, long nsecs) {
+    long p10, digit;
+
+    if (nsecs > 0) {
+        if (*cur < end)
+            *(*cur)++ = '.';
+        p10 = 100000000;
+        while (nsecs > 0) {
+            digit = nsecs / p10;
+            if (*cur < end)
+                *(*cur)++ = '0' + digit;
+            nsecs -= digit * p10;
+            p10 /= 10;
         }
+    }
+}
 
 /**
  * exsltDateFormatDuration:
@@ -1165,9 +1145,9 @@
 static xmlChar *
 exsltDateFormatDuration (const exsltDateDurValPtr dur)
 {
-    xmlChar buf[100], *cur = buf;
-    double secs, days;
-    double years, months;
+    xmlChar buf[100], *cur = buf, *end = buf + 99;
+    double secs, tmp;
+    long days, months, intSecs, nsecs;
 
     if (dur == NULL)
 	return NULL;
@@ -1177,9 +1157,8 @@
         return xmlStrdup((xmlChar*)"P0D");
 
     secs   = dur->sec;
-    days   = (double)dur->day;
-    years  = (double)(dur->mon / 12);
-    months = (double)(dur->mon % 12);
+    days   = dur->day;
+    months = dur->mon;
 
     *cur = '\0';
     if (days < 0) {
@@ -1190,10 +1169,6 @@
         days = -days;
         *cur = '-';
     }
-    if (years < 0) {
-        years = -years;
-        *cur = '-';
-    }
     if (months < 0) {
         months = -months;
         *cur = '-';
@@ -1203,23 +1178,64 @@
 
     *cur++ = 'P';
 
-    if (years != 0.0) {
-        FORMAT_ITEM(years, cur, 1, 'Y');
+    if (months >= 12) {
+        long years = months / 12;
+
+        months -= years * 12;
+        exsltFormatLong(&cur, end, years);
+        if (cur < end)
+            *cur++ = 'Y';
     }
 
-    if (months != 0.0) {
-        FORMAT_ITEM(months, cur, 1, 'M');
+    if (months != 0) {
+        exsltFormatLong(&cur, end, months);
+        if (cur < end)
+            *cur++ = 'M';
     }
 
-    FORMAT_ITEM(days, cur, 1, 'D');
-    if (secs > 0.0) {
-        *cur++ = 'T';
+    if (days != 0) {
+        exsltFormatLong(&cur, end, days);
+        if (cur < end)
+            *cur++ = 'D';
     }
-    FORMAT_ITEM(secs, cur, SECS_PER_HOUR, 'H');
-    FORMAT_ITEM(secs, cur, SECS_PER_MIN, 'M');
-    if (secs > 0.0) {
-        FORMAT_FLOAT(secs, cur, 0);
-        *cur++ = 'S';
+
+    tmp = floor(secs);
+    intSecs = (long) tmp;
+    /* Round to nearest to avoid issues with floating point precision */
+    nsecs = (long) floor((secs - tmp) * 1000000000 + 0.5);
+    if (nsecs >= 1000000000) {
+        nsecs -= 1000000000;
+        intSecs += 1;
+    }
+
+    if ((intSecs > 0) || (nsecs > 0)) {
+        if (cur < end)
+            *cur++ = 'T';
+
+        if (intSecs >= SECS_PER_HOUR) {
+            long hours = intSecs / SECS_PER_HOUR;
+
+            intSecs -= hours * SECS_PER_HOUR;
+            exsltFormatLong(&cur, end, hours);
+            if (cur < end)
+                *cur++ = 'H';
+        }
+
+        if (intSecs >= SECS_PER_MIN) {
+            long mins = intSecs / SECS_PER_MIN;
+
+            intSecs -= mins * SECS_PER_MIN;
+            exsltFormatLong(&cur, end, mins);
+            if (cur < end)
+                *cur++ = 'M';
+        }
+
+        if ((intSecs > 0) || (nsecs > 0)) {
+            exsltFormatLong(&cur, end, intSecs);
+            exsltFormatNanoseconds(&cur, end, nsecs);
+            if (cur < end)
+                *cur++ = 'S';
+        }
     }
 
     *cur = 0;
@@ -1227,6 +1243,42 @@
     return xmlStrdup(buf);
 }
 
+static void
+exsltFormatTwoDigits(xmlChar **cur, xmlChar *end, int num) {
+    if (num < 0 || num >= 100)
+        return;
+    if (*cur < end)
+        *(*cur)++ = '0' + num / 10;
+    if (*cur < end)
+        *(*cur)++ = '0' + num % 10;
+}
+
+static void
+exsltFormatTime(xmlChar **cur, xmlChar *end, exsltDateValPtr dt) {
+    double tmp;
+    long intSecs, nsecs;
+
+    exsltFormatTwoDigits(cur, end, dt->hour);
+    if (*cur < end)
+        *(*cur)++ = ':';
+
+    exsltFormatTwoDigits(cur, end, dt->min);
+    if (*cur < end)
+        *(*cur)++ = ':';
+
+    tmp = floor(dt->sec);
+    intSecs = (long) tmp;
+    /*
+     * Round to nearest to avoid issues with floating point precision,
+     * but don't carry over so seconds stay below 60.
+     */
+    nsecs = (long) floor((dt->sec - tmp) * 1000000000 + 0.5);
+    if (nsecs > 999999999)
+        nsecs = 999999999;
+    exsltFormatTwoDigits(cur, end, intSecs);
+    exsltFormatNanoseconds(cur, end, nsecs);
+}
+
 /**
  * exsltDateFormatDateTime:
  * @dt: an #exsltDateValPtr
@@ -1238,16 +1290,16 @@
 static xmlChar *
 exsltDateFormatDateTime (const exsltDateValPtr dt)
 {
-    xmlChar buf[100], *cur = buf;
+    xmlChar buf[100], *cur = buf, *end = buf + 99;
 
     if ((dt == NULL) ||	!VALID_DATETIME(dt))
 	return NULL;
 
-    FORMAT_DATE(dt, cur);
-    *cur = 'T';
-    cur++;
-    FORMAT_TIME(dt, cur);
-    FORMAT_TZ(dt->tzo, cur);
+    exsltFormatYearMonthDay(&cur, end, dt);
+    if (cur < end)
+        *cur++ = 'T';
+    exsltFormatTime(&cur, end, dt);
+    exsltFormatTimeZone(&cur, end, dt->tzo);
     *cur = 0;
 
     return xmlStrdup(buf);
@@ -1264,14 +1316,14 @@
 static xmlChar *
 exsltDateFormatDate (const exsltDateValPtr dt)
 {
-    xmlChar buf[100], *cur = buf;
+    xmlChar buf[100], *cur = buf, *end = buf + 99;
 
     if ((dt == NULL) || !VALID_DATETIME(dt))
 	return NULL;
 
-    FORMAT_DATE(dt, cur);
+    exsltFormatYearMonthDay(&cur, end, dt);
     if (dt->tz_flag || (dt->tzo != 0)) {
-	FORMAT_TZ(dt->tzo, cur);
+        exsltFormatTimeZone(&cur, end, dt->tzo);
     }
     *cur = 0;
 
@@ -1289,14 +1341,14 @@
 static xmlChar *
 exsltDateFormatTime (const exsltDateValPtr dt)
 {
-    xmlChar buf[100], *cur = buf;
+    xmlChar buf[100], *cur = buf, *end = buf + 99;
 
     if ((dt == NULL) || !VALID_TIME(dt))
 	return NULL;
 
-    FORMAT_TIME(dt, cur);
+    exsltFormatTime(&cur, end, dt);
     if (dt->tz_flag || (dt->tzo != 0)) {
-	FORMAT_TZ(dt->tzo, cur);
+        exsltFormatTimeZone(&cur, end, dt->tzo);
     }
     *cur = 0;
 
@@ -1316,7 +1368,6 @@
 static xmlChar *
 exsltDateFormat (const exsltDateValPtr dt)
 {
-
     if (dt == NULL)
 	return NULL;
 
@@ -1332,17 +1383,17 @@
     }
 
     if (dt->type & XS_GYEAR) {
-        xmlChar buf[100], *cur = buf;
+        xmlChar buf[100], *cur = buf, *end = buf + 99;
 
-        FORMAT_GYEAR(dt->year, cur);
+        exsltFormatGYear(&cur, end, dt->year);
         if (dt->type == XS_GYEARMONTH) {
-	    *cur = '-';
-	    cur++;
-	    FORMAT_GMONTH(dt->mon, cur);
+            if (cur < end)
+	        *cur++ = '-';
+            exsltFormat2Digits(&cur, end, dt->mon);
         }
 
         if (dt->tz_flag || (dt->tzo != 0)) {
-	    FORMAT_TZ(dt->tzo, cur);
+            exsltFormatTimeZone(&cur, end, dt->tzo);
         }
         *cur = 0;
         return xmlStrdup(buf);
@@ -1689,16 +1740,16 @@
 		      exsltDateDurValPtr y)
 {
     /* months */
-    if ((x->mon > 0 && y->mon > LONG_MAX - x->mon) ||
-        (x->mon < 0 && y->mon < LONG_MIN - x->mon)) {
+    if ((x->mon > 0 && y->mon >  LONG_MAX - x->mon) ||
+        (x->mon < 0 && y->mon <= LONG_MIN - x->mon)) {
         /* Overflow */
         return 0;
     }
     ret->mon = x->mon + y->mon;
 
     /* days */
-    if ((x->day > 0 && y->day > LONG_MAX - x->day) ||
-        (x->day < 0 && y->day < LONG_MIN - x->day)) {
+    if ((x->day > 0 && y->day >  LONG_MAX - x->day) ||
+        (x->day < 0 && y->day <= LONG_MIN - x->day)) {
         /* Overflow */
         return 0;
     }
diff --git a/third_party/libxslt/src/libexslt/functions.c b/third_party/libxslt/src/libexslt/functions.c
index 6babf0d..9497d7e 100644
--- a/third_party/libxslt/src/libexslt/functions.c
+++ b/third_party/libxslt/src/libexslt/functions.c
@@ -114,6 +114,7 @@
 	    xsltGenericError(xsltGenericErrorContext,
 		    "Failed to register function {%s}%s\n",
 		    URI, name);
+            xmlFree(func);
 	} else {		/* Do the registration */
 	    xsltGenericDebug(xsltGenericDebugContext,
 	            "exsltFuncRegisterImportFunc: register {%s}%s\n",
@@ -185,9 +186,11 @@
 		   void *vdata) {
     exsltFuncData *data = (exsltFuncData *) vdata;
 
-    if (data->result != NULL)
-	xmlXPathFreeObject(data->result);
-    xmlFree(data);
+    if (data != NULL) {
+        if (data->result != NULL)
+            xmlXPathFreeObject(data->result);
+        xmlFree(data);
+    }
 }
 
 /**
@@ -286,16 +289,12 @@
     exsltFuncData *data;
     exsltFuncFunctionData *func;
     xmlNodePtr paramNode, oldInsert, oldXPNode, fake;
-    int oldBase;
+    int oldBase, newBase;
     void *oldCtxtVar;
     xsltStackElemPtr params = NULL, param;
     xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
-    int i, notSet;
-    struct objChain {
-	struct objChain *next;
-	xmlXPathObjectPtr obj;
-    };
-    struct objChain	*savedObjChain = NULL, *savedObj;
+    int i;
+    xmlXPathObjectPtr *args = NULL;
 
     /*
      * retrieve func:function template
@@ -357,6 +356,10 @@
     /* Evaluating templates can change the XPath context node. */
     oldXPNode = tctxt->xpathCtxt->node;
 
+    fake = xmlNewDocNode(tctxt->output, NULL,
+			 (const xmlChar *)"fake", NULL);
+    if (fake == NULL)
+        goto error;
     /*
      * We have a problem with the evaluation of function parameters.
      * The original library code did not evaluate XPath expressions until
@@ -379,16 +382,15 @@
      * In order to give the function params and variables a new 'scope'
      * we change varsBase in the context.
      */
-    oldBase = tctxt->varsBase;
-    tctxt->varsBase = tctxt->varsNr;
+    newBase = tctxt->varsNr;
     /* If there are any parameters */
     if (paramNode != NULL) {
+        args = (xmlXPathObjectPtr *) xmlMalloc(sizeof(*args) * nargs);
+        if (args == NULL)
+            goto error;
         /* Fetch the stored argument values from the caller */
-	for (i = 0; i < nargs; i++) {
-	    savedObj = xmlMalloc(sizeof(struct objChain));
-	    savedObj->next = savedObjChain;
-	    savedObj->obj = valuePop(ctxt);
-	    savedObjChain = savedObj;
+	for (i = nargs - 1; i >= 0; i--) {
+            args[i] = valuePop(ctxt);
 	}
 
 	/*
@@ -405,17 +407,20 @@
 	 * as arguments from the caller
 	 * Calculate the number of un-set parameters
 	 */
-	notSet = func->nargs - nargs;
-	for (; i > 0; i--) {
+	for (i = 0; i < func->nargs; i++) {
 	    param = xsltParseStylesheetCallerParam (tctxt, paramNode);
-	    if (i > notSet) {	/* if parameter value set */
+            if (param == NULL) {
+                xsltLocalVariablePop(tctxt, newBase, -2);
+	        xsltFreeStackElemList(params);
+                for (; i < nargs; i++)
+                    xmlXPathFreeObject(args[i]);
+                goto error;
+            }
+	    if (i < nargs) {	/* if parameter value set */
 		param->computed = 1;
 		if (param->value != NULL)
 		    xmlXPathFreeObject(param->value);
-		savedObj = savedObjChain;	/* get next val from chain */
-		param->value = savedObj->obj;
-		savedObjChain = savedObjChain->next;
-		xmlFree(savedObj);
+		param->value = args[i];
 	    }
 	    xsltLocalVariablePush(tctxt, param, -1);
 	    param->next = params;
@@ -427,11 +432,11 @@
      * Actual processing. The context variable is cleared and restored
      * when func:result is evaluated.
      */
-    fake = xmlNewDocNode(tctxt->output, NULL,
-			 (const xmlChar *)"fake", NULL);
+    oldBase = tctxt->varsBase;
     oldInsert = tctxt->insert;
     oldCtxtVar = data->ctxtVar;
     data->ctxtVar = tctxt->contextVariable;
+    tctxt->varsBase = newBase;
     tctxt->insert = fake;
     tctxt->contextVariable = NULL;
     xsltApplyOneTemplate (tctxt, tctxt->node,
@@ -470,14 +475,14 @@
 			 "{%s}%s: cannot write to result tree while "
 			 "executing a function\n",
 			 ctxt->context->functionURI, ctxt->context->function);
-	xmlFreeNode(fake);
         xmlXPathFreeObject(ret);
 	goto error;
     }
-    xmlFreeNode(fake);
     valuePush(ctxt, ret);
 
 error:
+    xmlFree(args);
+    xmlFreeNode(fake);
     tctxt->depth--;
 }
 
diff --git a/third_party/libxslt/src/libexslt/saxon.c b/third_party/libxslt/src/libexslt/saxon.c
index 8512c6d..6166b15 100644
--- a/third_party/libxslt/src/libexslt/saxon.c
+++ b/third_party/libxslt/src/libexslt/saxon.c
@@ -98,13 +98,18 @@
     ret = xmlHashLookup(hash, arg);
 
     if (ret == NULL) {
-	 ret = xmlXPathCtxtCompile(tctxt->xpathCtxt, arg);
-	 if (ret == NULL) {
-	      xmlFree(arg);
-              xmlXPathSetError(ctxt, XPATH_EXPR_ERROR);
-	      return;
-	 }
-	 xmlHashAddEntry(hash, arg, (void *) ret);
+        ret = xmlXPathCtxtCompile(tctxt->xpathCtxt, arg);
+        if (ret == NULL) {
+            xmlFree(arg);
+            xmlXPathSetError(ctxt, XPATH_EXPR_ERROR);
+            return;
+        }
+        if (xmlHashAddEntry(hash, arg, (void *) ret) < 0) {
+            xmlXPathFreeCompExpr(ret);
+            xmlFree(arg);
+            xmlXPathSetError(ctxt, XPATH_MEMORY_ERROR);
+            return;
+        }
     }
 
     xmlFree(arg);
diff --git a/third_party/libxslt/src/libexslt/sets.c b/third_party/libxslt/src/libexslt/sets.c
index a4b5546..357fe2c0 100644
--- a/third_party/libxslt/src/libexslt/sets.c
+++ b/third_party/libxslt/src/libexslt/sets.c
@@ -112,13 +112,15 @@
     /* !!! must be sorted !!! */
     ret = xmlXPathDistinctSorted(ns);
 
-	if (ret != ns)
-		xmlXPathFreeNodeSet(ns);
+    if (ret != ns)
+	xmlXPathFreeNodeSet(ns);
 
     obj = xmlXPathWrapNodeSet(ret);
-    obj->user = user;
-    obj->boolval = boolval;
-    valuePush((ctxt), obj);
+    if (obj != NULL) {
+        obj->user = user;
+        obj->boolval = boolval;
+    }
+    valuePush(ctxt, obj);
 }
 
 /**
diff --git a/third_party/libxslt/src/libxslt/attributes.c b/third_party/libxslt/src/libxslt/attributes.c
index 28a0841..4cc49d0d 100644
--- a/third_party/libxslt/src/libxslt/attributes.c
+++ b/third_party/libxslt/src/libxslt/attributes.c
@@ -87,6 +87,7 @@
 struct _xsltAttrSetContext {
     xsltStylesheetPtr topStyle;
     xsltStylesheetPtr style;
+    int error;
 };
 
 static void
@@ -421,9 +422,12 @@
     set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
     if (set == NULL) {
         set = xsltNewAttrSet();
-        if (set == NULL)
+        if ((set == NULL) ||
+            (xmlHashAddEntry2(style->attributeSets, ncname, nsUri, set) < 0)) {
+	    xsltGenericError(xsltGenericErrorContext, "memory error\n");
+            xsltFreeAttrSet(set);
             return;
-        xmlHashAddEntry2(style->attributeSets, ncname, nsUri, set);
+        }
     }
 
     /*
@@ -663,6 +667,12 @@
     xsltStylesheetPtr topStyle = asctx->topStyle;
     xsltStylesheetPtr style = asctx->style;
 
+    if (asctx->error) {
+        if (style != topStyle)
+            xsltFreeAttrSet(set);
+        return;
+    }
+
     xsltResolveAttrSet(set, topStyle, style, name, ns, 1);
 
     /* Move attribute sets to top stylesheet. */
@@ -675,6 +685,8 @@
 	    xsltGenericError(xsltGenericErrorContext,
                 "xsl:attribute-set : internal error, can't move imported "
                 " attribute set %s\n", name);
+            asctx->error = 1;
+            xsltFreeAttrSet(set);
         }
     }
 }
@@ -695,6 +707,7 @@
 	    "Resolving attribute sets references\n");
 #endif
     asctx.topStyle = style;
+    asctx.error = 0;
     cur = style;
     while (cur != NULL) {
 	if (cur->attributeSets != NULL) {
diff --git a/third_party/libxslt/src/libxslt/attrvt.c b/third_party/libxslt/src/libxslt/attrvt.c
index a885526..6157fcd 100644
--- a/third_party/libxslt/src/libxslt/attrvt.c
+++ b/third_party/libxslt/src/libxslt/attrvt.c
@@ -155,10 +155,8 @@
         size_t size = sizeof(xsltAttrVT) +
                       (avt->max_seg + MAX_AVT_SEG) * sizeof(void *);
 	xsltAttrVTPtr tmp = (xsltAttrVTPtr) xmlRealloc(avt, size);
-	if (tmp == NULL) {
-            xsltFreeAttrVT(avt);
+	if (tmp == NULL)
 	    return NULL;
-	}
         avt = tmp;
 	memset(&avt->segments[avt->nb_seg], 0, MAX_AVT_SEG*sizeof(void *));
 	avt->max_seg += MAX_AVT_SEG;
@@ -182,6 +180,7 @@
     const xmlChar *cur;
     xmlChar *ret = NULL;
     xmlChar *expr = NULL;
+    xmlXPathCompExprPtr comp = NULL;
     xsltAttrVTPtr avt;
     int i = 0, lastavt = 0;
 
@@ -280,8 +279,6 @@
 	        XSLT_TODO
 		goto error;
 	    } else {
-		xmlXPathCompExprPtr comp;
-
 		comp = xsltXPathCompile(style, expr);
 		if (comp == NULL) {
 		    xsltTransformError(NULL, style, attr->parent,
@@ -293,14 +290,21 @@
 		if (avt->nb_seg == 0)
 		    avt->strstart = 0;
 		if (lastavt == 1) {
-		    if ((avt = xsltSetAttrVTsegment(avt, NULL)) == NULL)
+		    if ((avt = xsltSetAttrVTsegment(avt, NULL)) == NULL) {
+                        xsltTransformError(NULL, style, attr->parent,
+                                           "out of memory\n");
 		        goto error;
+                    }
 		}
-		if ((avt = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL)
+		if ((avt = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL) {
+                    xsltTransformError(NULL, style, attr->parent,
+                                       "out of memory\n");
 		    goto error;
+                }
 		lastavt = 1;
 		xmlFree(expr);
 		expr = NULL;
+                comp = NULL;
 	    }
 	    cur++;
 	    str = cur;
@@ -350,6 +354,8 @@
 	xmlFree(ret);
     if (expr != NULL)
 	xmlFree(expr);
+    if (comp != NULL)
+        xmlXPathFreeCompExpr(comp);
 }
 
 
diff --git a/third_party/libxslt/src/libxslt/documents.c b/third_party/libxslt/src/libxslt/documents.c
index 4aad11bb..51865b85 100644
--- a/third_party/libxslt/src/libxslt/documents.c
+++ b/third_party/libxslt/src/libxslt/documents.c
@@ -399,6 +399,8 @@
 	return(NULL);
 
     ret = xsltNewStyleDocument(style, doc);
+    if (ret == NULL)
+        xmlFreeDoc(doc);
     return(ret);
 }
 
diff --git a/third_party/libxslt/src/libxslt/extensions.c b/third_party/libxslt/src/libxslt/extensions.c
index ea5e835..f9ca08e 100644
--- a/third_party/libxslt/src/libxslt/extensions.c
+++ b/third_party/libxslt/src/libxslt/extensions.c
@@ -28,6 +28,7 @@
 #include <libxml/xmlIO.h>
 #include "xslt.h"
 #include "xsltInternals.h"
+#include "xsltlocale.h"
 #include "xsltutils.h"
 #include "imports.h"
 #include "extensions.h"
@@ -738,8 +739,11 @@
     * Store the user-data in the context of the given stylesheet.
     */
     dataContainer = xsltNewExtData(module, userData);
-    if (dataContainer == NULL)
+    if (dataContainer == NULL) {
+	if (module->styleShutdownFunc)
+	    module->styleShutdownFunc(style, URI, userData);
 	return (NULL);
+    }
 
     if (xmlHashAddEntry(style->extInfos, URI,
 	(void *) dataContainer) < 0)
@@ -920,9 +924,8 @@
                 return (NULL);
 
             data = xsltNewExtData(module, extData);
-            if (data == NULL)
-                return (NULL);
-            if (xmlHashAddEntry(ctxt->extInfos, URI, (void *) data) < 0) {
+            if ((data == NULL) ||
+                (xmlHashAddEntry(ctxt->extInfos, URI, (void *) data) < 0)) {
                 xsltTransformError(ctxt, NULL, NULL,
                                    "Failed to register module data: %s\n",
                                    URI);
@@ -994,6 +997,8 @@
     }
     ctxtData = xsltNewExtData(module, extData);
     if (ctxtData == NULL) {
+        if (module->shutdownFunc)
+            module->shutdownFunc(ctxt->ctxt, URI, extData);
         ctxt->ret = -1;
         return;
     }
@@ -1001,6 +1006,9 @@
     if (ctxt->ctxt->extInfos == NULL)
         ctxt->ctxt->extInfos = xmlHashCreate(10);
     if (ctxt->ctxt->extInfos == NULL) {
+        if (module->shutdownFunc)
+            module->shutdownFunc(ctxt->ctxt, URI, extData);
+        xsltFreeExtData(ctxtData);
         ctxt->ret = -1;
         return;
     }
diff --git a/third_party/libxslt/src/libxslt/functions.c b/third_party/libxslt/src/libxslt/functions.c
index 61ce190..061ef7b2 100644
--- a/third_party/libxslt/src/libxslt/functions.c
+++ b/third_party/libxslt/src/libxslt/functions.c
@@ -241,14 +241,14 @@
         obj2 = valuePop(ctxt);
     }
 
-    if (ctxt->value->type == XPATH_NODESET) {
+    if ((ctxt->value != NULL) && (ctxt->value->type == XPATH_NODESET)) {
         int i;
         xmlXPathObjectPtr newobj, ret;
 
         obj = valuePop(ctxt);
         ret = xmlXPathNewNodeSet(NULL);
 
-        if ((obj != NULL) && obj->nodesetval) {
+        if ((obj != NULL) && (obj->nodesetval != NULL) && (ret != NULL)) {
             for (i = 0; i < obj->nodesetval->nodeNr; i++) {
                 valuePush(ctxt,
                           xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
@@ -260,11 +260,15 @@
                               xmlXPathNewNodeSet(obj->nodesetval->
                                                  nodeTab[i]));
                 }
+                if (ctxt->error)
+                    break;
                 xsltDocumentFunction(ctxt, 2);
                 newobj = valuePop(ctxt);
-                ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
-                                                       newobj->nodesetval);
-                xmlXPathFreeObject(newobj);
+                if (newobj != NULL) {
+                    ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
+                                                           newobj->nodesetval);
+                    xmlXPathFreeObject(newobj);
+                }
             }
         }
 
@@ -279,7 +283,7 @@
      * Make sure it's converted to a string
      */
     xmlXPathStringFunction(ctxt, 1);
-    if (ctxt->value->type != XPATH_STRING) {
+    if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
         xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
                          "document() : invalid arg expecting a string\n");
         ctxt->error = XPATH_INVALID_TYPE;
@@ -378,6 +382,12 @@
 	xmlXPathObjectPtr newobj, ret;
 
 	ret = xmlXPathNewNodeSet(NULL);
+        if (ret == NULL) {
+            ctxt->error = XPATH_MEMORY_ERROR;
+            xmlXPathFreeObject(obj1);
+            xmlXPathFreeObject(obj2);
+            return;
+        }
 
 	if (obj2->nodesetval != NULL) {
 	    for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
@@ -387,8 +397,9 @@
 		xmlXPathStringFunction(ctxt, 1);
 		xsltKeyFunction(ctxt, 2);
 		newobj = valuePop(ctxt);
-		ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
-						       newobj->nodesetval);
+                if (newobj != NULL)
+		    ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
+						           newobj->nodesetval);
 		xmlXPathFreeObject(newobj);
 	    }
 	}
@@ -445,13 +456,13 @@
 	 */
 	valuePush(ctxt, obj2);
 	xmlXPathStringFunction(ctxt, 1);
-	if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
+	obj2 = valuePop(ctxt);
+	if ((obj2 == NULL) || (obj2->type != XPATH_STRING)) {
 	    xsltTransformError(tctxt, NULL, tctxt->inst,
 		"key() : invalid arg expecting a string\n");
 	    ctxt->error = XPATH_INVALID_TYPE;
 	    goto error;
 	}
-	obj2 = valuePop(ctxt);
 	value = obj2->stringval;
 
 	/*
@@ -554,6 +565,10 @@
     obj = valuePop(ctxt);
     if (obj->type != XPATH_STRING) {
 	obj = xmlXPathConvertString(obj);
+        if (obj == NULL) {
+            xmlXPathErr(ctxt, XPATH_MEMORY_ERROR);
+            return;
+        }
     }
 
     str = obj->stringval;
@@ -646,7 +661,8 @@
         return;
     }
 
-    if (formatValues != NULL) {
+    if ((ctxt->error == 0) &&
+        (formatValues != NULL) && (formatObj != NULL) && (numberObj != NULL)) {
 	if (xsltFormatNumberConversion(formatValues,
 				       formatObj->stringval,
 				       numberObj->floatval,
diff --git a/third_party/libxslt/src/libxslt/imports.c b/third_party/libxslt/src/libxslt/imports.c
index 4f5de03..bf2196e 100644
--- a/third_party/libxslt/src/libxslt/imports.c
+++ b/third_party/libxslt/src/libxslt/imports.c
@@ -53,6 +53,35 @@
     }
 }
 
+static int
+xsltCheckCycle(xsltStylesheetPtr style, const xmlChar *URI) {
+    xsltStylesheetPtr ancestor;
+    xsltDocumentPtr docptr;
+
+    /*
+     * in order to detect recursion, we check all previously included
+     * stylesheets.
+     */
+    docptr = style->includes;
+    while (docptr != NULL) {
+        if (xmlStrEqual(docptr->doc->URL, URI))
+	    return(-1);
+	docptr = docptr->includes;
+    }
+
+    /*
+     * Also check imported stylesheets.
+     */
+    ancestor = style;
+    while (ancestor != NULL) {
+	if (xmlStrEqual(ancestor->doc->URL, URI))
+	    return(-1);
+	ancestor = ancestor->parent;
+    }
+
+    return(0);
+}
+
 /**
  * xsltParseStylesheetImport:
  * @style:  the XSLT stylesheet
@@ -91,16 +120,10 @@
 	goto error;
     }
 
-    res = style;
-    while (res != NULL) {
-        if (res->doc == NULL)
-	    break;
-	if (xmlStrEqual(res->doc->URL, URI)) {
-	    xsltTransformError(NULL, style, cur,
-	       "xsl:import : recursion detected on imported URL %s\n", URI);
-	    goto error;
-	}
-	res = res->parent;
+    if (xsltCheckCycle(style, URI) < 0) {
+        xsltTransformError(NULL, style, cur,
+           "xsl:import : recursion detected on imported URL %s\n", URI);
+        goto error;
     }
 
     /*
@@ -170,7 +193,6 @@
     xmlChar *URI = NULL;
     xsltStylesheetPtr result;
     xsltDocumentPtr include;
-    xsltDocumentPtr docptr;
     int oldNopreproc;
 
     if ((cur == NULL) || (style == NULL))
@@ -191,18 +213,10 @@
 	goto error;
     }
 
-    /*
-     * in order to detect recursion, we check all previously included
-     * stylesheets.
-     */
-    docptr = style->includes;
-    while (docptr != NULL) {
-        if (xmlStrEqual(docptr->doc->URL, URI)) {
-	    xsltTransformError(NULL, style, cur,
-	        "xsl:include : recursion detected on included URL %s\n", URI);
-	    goto error;
-	}
-	docptr = docptr->includes;
+    if (xsltCheckCycle(style, URI) < 0) {
+        xsltTransformError(NULL, style, cur,
+            "xsl:include : recursion detected on included URL %s\n", URI);
+        goto error;
     }
 
     include = xsltLoadStyleDocument(style, URI);
diff --git a/third_party/libxslt/src/libxslt/keys.c b/third_party/libxslt/src/libxslt/keys.c
index fb18cd5..f23cc4e9 100644
--- a/third_party/libxslt/src/libxslt/keys.c
+++ b/third_party/libxslt/src/libxslt/keys.c
@@ -294,6 +294,8 @@
 #endif
 
     key = xsltNewKeyDef(name, nameURI);
+    if (key == NULL)
+        return(-1);
     key->match = xmlStrdup(match);
     key->use = xmlStrdup(use);
     key->inst = inst;
@@ -827,7 +829,10 @@
 		keylist = xmlXPathNodeSetCreate(cur);
 		if (keylist == NULL)
 		    goto error;
-		xmlHashAddEntry(table->keys, str, keylist);
+		if (xmlHashAddEntry(table->keys, str, keylist) < 0) {
+                    xmlXPathFreeNodeSet(keylist);
+                    goto error;
+                }
 	    } else {
 		/*
 		* TODO: How do we know if this function failed?
diff --git a/third_party/libxslt/src/libxslt/numbers.c b/third_party/libxslt/src/libxslt/numbers.c
index 9e496bb..e0ae625 100644
--- a/third_party/libxslt/src/libxslt/numbers.c
+++ b/third_party/libxslt/src/libxslt/numbers.c
@@ -116,7 +116,7 @@
 #define IS_DIGIT_ONE(x) xsltIsDigitZero((x)-1)
 
 static int
-xsltIsDigitZero(unsigned int ch)
+xsltIsDigitZero(int ch)
 {
     /*
      * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
@@ -985,32 +985,12 @@
 		"Invalid format (0-length)\n");
     }
     *result = NULL;
-    switch (xmlXPathIsInf(number)) {
-	case -1:
-	    if (self->minusSign == NULL)
-		*result = xmlStrdup(BAD_CAST "-");
-	    else
-		*result = xmlStrdup(self->minusSign);
-	    /* Intentional fall-through */
-	case 1:
-	    if ((self == NULL) || (self->infinity == NULL))
-		*result = xmlStrcat(*result, BAD_CAST "Infinity");
-	    else
-		*result = xmlStrcat(*result, self->infinity);
-	    return(status);
-	default:
-	    if (xmlXPathIsNaN(number)) {
-		if ((self == NULL) || (self->noNumber == NULL))
-		    *result = xmlStrdup(BAD_CAST "NaN");
-		else
-		    *result = xmlStrdup(self->noNumber);
-		return(status);
-	    }
-    }
-
-    buffer = xmlBufferCreate();
-    if (buffer == NULL) {
-	return XPATH_MEMORY_ERROR;
+    if (xmlXPathIsNaN(number)) {
+        if ((self == NULL) || (self->noNumber == NULL))
+            *result = xmlStrdup(BAD_CAST "NaN");
+        else
+            *result = xmlStrdup(self->noNumber);
+        return(status);
     }
 
     format_info.integer_hash = 0;
@@ -1283,6 +1263,30 @@
 	format_info.add_decimal = TRUE;
     }
 
+    /* Apply multiplier */
+    number *= (double)format_info.multiplier;
+    switch (xmlXPathIsInf(number)) {
+	case -1:
+	    if (self->minusSign == NULL)
+		*result = xmlStrdup(BAD_CAST "-");
+	    else
+		*result = xmlStrdup(self->minusSign);
+	    /* Intentional fall-through */
+	case 1:
+	    if ((self == NULL) || (self->infinity == NULL))
+		*result = xmlStrcat(*result, BAD_CAST "Infinity");
+	    else
+		*result = xmlStrcat(*result, self->infinity);
+	    return(status);
+	default:
+            break;
+    }
+
+    buffer = xmlBufferCreate();
+    if (buffer == NULL) {
+	return XPATH_MEMORY_ERROR;
+    }
+
     /* Ready to output our number.  First see if "default sign" is required */
     if (default_sign != 0)
 	xmlBufferAdd(buffer, self->minusSign, xmlUTF8Strsize(self->minusSign, 1));
@@ -1297,10 +1301,13 @@
         j += len;
     }
 
-    /* Next do the integer part of the number */
-    number = fabs(number) * (double)format_info.multiplier;
+    /* Round to n digits */
+    number = fabs(number);
     scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash));
-    number = floor((scale * number + 0.5)) / scale;
+    number += .5 / scale;
+    number -= fmod(number, 1 / scale);
+
+    /* Next do the integer part of the number */
     if ((self->grouping != NULL) &&
         (self->grouping[0] != 0)) {
         int gchar;
diff --git a/third_party/libxslt/src/libxslt/pattern.c b/third_party/libxslt/src/libxslt/pattern.c
index 0a6fd92..6e99b12 100644
--- a/third_party/libxslt/src/libxslt/pattern.c
+++ b/third_party/libxslt/src/libxslt/pattern.c
@@ -311,10 +311,6 @@
 	     "xsltCompMatchAdd: memory re-allocation failure.\n");
 	    if (ctxt->style != NULL)
 		ctxt->style->errors++;
-	    if (value)
-	        xmlFree(value);
-	    if (value2)
-	        xmlFree(value2);
 	    return (-1);
 	}
         comp->maxStep *= 2;
@@ -483,16 +479,12 @@
 static int
 xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states,
                  int step, xmlNodePtr node) {
-    if ((states->states == NULL) || (states->maxstates <= 0)) {
-        states->maxstates = 4;
-	states->nbstates = 0;
-	states->states = xmlMalloc(4 * sizeof(xsltStepState));
-    }
-    else if (states->maxstates <= states->nbstates) {
+    if (states->maxstates <= states->nbstates) {
         xsltStepState *tmp;
+        int newMax = states->maxstates == 0 ? 4 : 2 * states->maxstates;
 
 	tmp = (xsltStepStatePtr) xmlRealloc(states->states,
-			       2 * states->maxstates * sizeof(xsltStepState));
+                newMax * sizeof(xsltStepState));
 	if (tmp == NULL) {
 	    xsltGenericError(xsltGenericErrorContext,
 	     "xsltPatPushState: memory re-allocation failure.\n");
@@ -500,7 +492,7 @@
 	    return(-1);
 	}
 	states->states = tmp;
-	states->maxstates *= 2;
+	states->maxstates = newMax;
     }
     states->states[states->nbstates].step = step;
     states->states[states->nbstates++].node = node;
@@ -1512,6 +1504,7 @@
     xmlChar *name = NULL;
     const xmlChar *URI = NULL;
     xmlChar *URL = NULL;
+    xmlChar *ret = NULL;
     int level;
     xsltAxis axis = 0;
 
@@ -1588,7 +1581,6 @@
 		    xsltTransformError(NULL, NULL, NULL,
 			    "xsltCompileStepPattern : Name expected\n");
 		    ctxt->error = 1;
-                    xmlFree(URL);
 		    goto error;
 		}
 	    } else {
@@ -1651,7 +1643,6 @@
     level = 0;
     while (CUR == '[') {
 	const xmlChar *q;
-	xmlChar *ret = NULL;
 
 	level++;
 	NEXT;
@@ -1695,6 +1686,10 @@
 	xmlFree(token);
     if (name != NULL)
 	xmlFree(name);
+    if (URL != NULL)
+	xmlFree(URL);
+    if (ret != NULL)
+	xmlFree(ret);
 }
 
 /**
@@ -2087,6 +2082,8 @@
     if (pat == NULL)
 	return(-1);
     while (pat) {
+        int success = 0;
+
 	next = pat->next;
 	pat->next = NULL;
 	name = NULL;
@@ -2158,17 +2155,15 @@
 	if (name != NULL) {
 	    if (style->templatesHash == NULL) {
 		style->templatesHash = xmlHashCreate(1024);
-		if (style->templatesHash == NULL) {
-		    xsltFreeCompMatch(pat);
-		    return(-1);
-		}
-		xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
+		success = (style->templatesHash != NULL) &&
+                          (xmlHashAddEntry3(style->templatesHash, name, mode,
+                                            modeURI, pat) >= 0);
 	    } else {
 		list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
 							 name, mode, modeURI);
 		if (list == NULL) {
-		    xmlHashAddEntry3(style->templatesHash, name,
-				     mode, modeURI, pat);
+		    success = (xmlHashAddEntry3(style->templatesHash, name,
+				                mode, modeURI, pat) >= 0);
 		} else {
 		    /*
 		     * Note '<=' since one must choose among the matching
@@ -2188,6 +2183,7 @@
 			pat->next = list->next;
 			list->next = pat;
 		    }
+                    success = 1;
 		}
 	    }
 	} else if (top != NULL) {
@@ -2207,10 +2203,13 @@
 		pat->next = list->next;
 		list->next = pat;
 	    }
-	} else {
+            success = 1;
+	}
+        if (success == 0) {
 	    xsltTransformError(NULL, style, NULL,
 			     "xsltAddTemplate: invalid compiled pattern\n");
 	    xsltFreeCompMatch(pat);
+            xsltFreeCompMatchList(next);
 	    return(-1);
 	}
 #ifdef WITH_XSLT_DEBUG_PATTERN
diff --git a/third_party/libxslt/src/libxslt/preproc.c b/third_party/libxslt/src/libxslt/preproc.c
index 7d2fa22..fae69a09 100644
--- a/third_party/libxslt/src/libxslt/preproc.c
+++ b/third_party/libxslt/src/libxslt/preproc.c
@@ -392,8 +392,6 @@
             break;
         case XSLT_FUNC_SORT: {
 		xsltStyleItemSortPtr item = (xsltStyleItemSortPtr) comp;
-		if (item->locale != (xsltLocale)0)
-		    xsltFreeLocale(item->locale);
 		if (item->comp != NULL)
 		    xmlXPathFreeCompExpr(item->comp);
 	    }
@@ -496,8 +494,6 @@
 	    break;
     }
 #else
-    if (comp->locale != (xsltLocale)0)
-	xsltFreeLocale(comp->locale);
     if (comp->comp != NULL)
 	xmlXPathFreeCompExpr(comp->comp);
     if (comp->numdata.countPat != NULL)
@@ -743,12 +739,6 @@
     comp->lang = xsltEvalStaticAttrValueTemplate(style, inst,
 				 (const xmlChar *)"lang",
 				 NULL, &comp->has_lang);
-    if (comp->lang != NULL) {
-	comp->locale = xsltNewLocale(comp->lang);
-    }
-    else {
-        comp->locale = (xsltLocale)0;
-    }
 
     comp->select = xsltGetCNsProp(style, inst,(const xmlChar *)"select", XSLT_NAMESPACE);
     if (comp->select == NULL) {
diff --git a/third_party/libxslt/src/libxslt/templates.c b/third_party/libxslt/src/libxslt/templates.c
index 4108ed2..f08b9bda 100644
--- a/third_party/libxslt/src/libxslt/templates.c
+++ b/third_party/libxslt/src/libxslt/templates.c
@@ -151,7 +151,7 @@
     if (res != NULL) {
 	if (res->type != XPATH_STRING)
 	    res = xmlXPathConvertString(res);
-	if (res->type == XPATH_STRING) {
+	if ((res != NULL) && (res->type == XPATH_STRING)) {
             ret = res->stringval;
 	    res->stringval = NULL;
 	} else {
@@ -229,7 +229,7 @@
     insert = xmlNewDocNode(ctxt->output, NULL,
 	                   (const xmlChar *)"fake", NULL);
     if (insert == NULL) {
-	xsltTransformError(ctxt, NULL, contextNode,
+	xsltTransformError(ctxt, NULL, inst,
 		"Failed to create temporary node\n");
 	return(NULL);
     }
diff --git a/third_party/libxslt/src/libxslt/transform.c b/third_party/libxslt/src/libxslt/transform.c
index 67d041a..0fc0400 100644
--- a/third_party/libxslt/src/libxslt/transform.c
+++ b/third_party/libxslt/src/libxslt/transform.c
@@ -40,6 +40,7 @@
 #include "xslt.h"
 #include "xsltInternals.h"
 #include "xsltutils.h"
+#include "xsltlocale.h"
 #include "pattern.h"
 #include "transform.h"
 #include "variables.h"
@@ -119,26 +120,18 @@
 static int
 templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value)
 {
-    if (ctxt->templMax == 0) {
-        ctxt->templMax = 4;
-        ctxt->templTab =
-            (xsltTemplatePtr *) xmlMalloc(ctxt->templMax *
-                                          sizeof(ctxt->templTab[0]));
-        if (ctxt->templTab == NULL) {
-            xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
-            return (0);
-        }
-    }
-    else if (ctxt->templNr >= ctxt->templMax) {
-        ctxt->templMax *= 2;
-        ctxt->templTab =
-            (xsltTemplatePtr *) xmlRealloc(ctxt->templTab,
-                                           ctxt->templMax *
-                                           sizeof(ctxt->templTab[0]));
-        if (ctxt->templTab == NULL) {
+    if (ctxt->templNr >= ctxt->templMax) {
+        xsltTemplatePtr *tmp;
+        int newMax = ctxt->templMax == 0 ? 4 : ctxt->templMax * 2;
+
+        tmp = (xsltTemplatePtr *) xmlRealloc(ctxt->templTab,
+                newMax * sizeof(*tmp));
+        if (tmp == NULL) {
             xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
             return (0);
         }
+        ctxt->templTab = tmp;
+        ctxt->templMax = newMax;
     }
     ctxt->templTab[ctxt->templNr] = value;
     ctxt->templ = value;
@@ -706,6 +699,10 @@
     cur->xinclude = xsltGetXIncludeDefault();
     cur->keyInitLevel = 0;
 
+    cur->newLocale = xsltNewLocale;
+    cur->freeLocale = xsltFreeLocale;
+    cur->genSortKey = xsltStrxfrm;
+
     return(cur);
 
 internal_err:
@@ -716,7 +713,7 @@
 
 /**
  * xsltFreeTransformContext:
- * @ctxt:  an XSLT parser context
+ * @ctxt:  an XSLT transform context
  *
  * Free up the memory allocated by @ctxt
  */
@@ -1090,8 +1087,10 @@
 	if (xmlDictOwns(ctxt->dict, cur->content))
 	    copy->content = cur->content;
 	else {
-	    if ((copy->content = xmlStrdup(cur->content)) == NULL)
+	    if ((copy->content = xmlStrdup(cur->content)) == NULL) {
+                xmlFreeNode(copy);
 		return NULL;
+            }
 	}
 
 	ctxt->lasttext = NULL;
@@ -2224,26 +2223,18 @@
 		      xsltStackElemPtr variable,
 		      int level)
 {
-    if (ctxt->varsMax == 0) {
-	ctxt->varsMax = 10;
-	ctxt->varsTab =
-	    (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax *
-	    sizeof(ctxt->varsTab[0]));
-	if (ctxt->varsTab == NULL) {
-	    xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
-	    return (-1);
-	}
-    }
     if (ctxt->varsNr >= ctxt->varsMax) {
-	ctxt->varsMax *= 2;
-	ctxt->varsTab =
-	    (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
-	    ctxt->varsMax *
-	    sizeof(ctxt->varsTab[0]));
-	if (ctxt->varsTab == NULL) {
+        xsltStackElemPtr *tmp;
+        int newMax = ctxt->varsMax == 0 ? 10 : 2 * ctxt->varsMax;
+
+	tmp = (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
+                newMax * sizeof(*tmp));
+	if (tmp == NULL) {
 	    xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
 	    return (-1);
 	}
+        ctxt->varsTab = tmp;
+        ctxt->varsMax = newMax;
     }
     ctxt->varsTab[ctxt->varsNr++] = variable;
     ctxt->vars = variable;
@@ -5776,6 +5767,8 @@
         if (cur->children != NULL && cur->type != XML_ENTITY_REF_NODE) {
             cur = cur->children;
         } else {
+            if (cur == (xmlNodePtr) doc)
+                return;
             while (cur->next == NULL) {
                 cur = cur->parent;
                 if (cur == (xmlNodePtr) doc)
diff --git a/third_party/libxslt/src/libxslt/variables.c b/third_party/libxslt/src/libxslt/variables.c
index 8ce33d6..c0055c2 100644
--- a/third_party/libxslt/src/libxslt/variables.c
+++ b/third_party/libxslt/src/libxslt/variables.c
@@ -757,26 +757,18 @@
 	return(-1);
 
     do {
-	if (ctxt->varsMax == 0) {
-	    ctxt->varsMax = 10;
-	    ctxt->varsTab =
-		(xsltStackElemPtr *) xmlMalloc(ctxt->varsMax *
-		sizeof(ctxt->varsTab[0]));
-	    if (ctxt->varsTab == NULL) {
-		xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
-		return (-1);
-	    }
-	}
 	if (ctxt->varsNr >= ctxt->varsMax) {
-	    ctxt->varsMax *= 2;
-	    ctxt->varsTab =
-		(xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
-		ctxt->varsMax *
-		sizeof(ctxt->varsTab[0]));
-	    if (ctxt->varsTab == NULL) {
-		xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
-		return (-1);
-	    }
+            xsltStackElemPtr *tmp;
+            int newMax = ctxt->varsMax == 0 ? 10 : 2 * ctxt->varsMax;
+
+            tmp = (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
+                    newMax * sizeof(*tmp));
+            if (tmp == NULL) {
+                xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
+                return (-1);
+            }
+            ctxt->varsTab = tmp;
+            ctxt->varsMax = newMax;
 	}
 	ctxt->varsTab[ctxt->varsNr++] = elem;
 	ctxt->vars = elem;
@@ -1318,8 +1310,13 @@
 	    if (def == NULL) {
 
 		def = xsltCopyStackElem(elem);
-		xmlHashAddEntry2(ctxt->globalVars,
-				 elem->name, elem->nameURI, def);
+		if (xmlHashAddEntry2(ctxt->globalVars,
+				     elem->name, elem->nameURI, def) < 0) {
+                    xmlGenericError(xmlGenericErrorContext,
+                                    "hash update failed\n");
+                    xsltFreeStackElem(def);
+                    return(-1);
+                }
 	    } else if ((elem->comp != NULL) &&
 		       (elem->comp->type == XSLT_FUNC_VARIABLE)) {
 		/*
@@ -1877,7 +1874,10 @@
 #endif /* else of XSLT_REFACTORED */
 
     variable = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree);
-    xsltAddStackElem(ctxt, variable);
+    if (xsltAddStackElem(ctxt, variable) < 0) {
+        xsltFreeStackElem(variable);
+        return(-1);
+    }
     return(0);
 }
 
diff --git a/third_party/libxslt/src/libxslt/xslt.c b/third_party/libxslt/src/libxslt/xslt.c
index e19f5296..c17faa6 100644
--- a/third_party/libxslt/src/libxslt/xslt.c
+++ b/third_party/libxslt/src/libxslt/xslt.c
@@ -151,31 +151,23 @@
 {
     int i;
 
-    if (style->exclPrefixMax == 0) {
-        style->exclPrefixMax = 4;
-        style->exclPrefixTab =
-            (xmlChar * *)xmlMalloc(style->exclPrefixMax *
-                                   sizeof(style->exclPrefixTab[0]));
-        if (style->exclPrefixTab == NULL) {
-            xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
-            return (-1);
-        }
-    }
     /* do not push duplicates */
     for (i = 0;i < style->exclPrefixNr;i++) {
         if (xmlStrEqual(style->exclPrefixTab[i], value))
 	    return(-1);
     }
     if (style->exclPrefixNr >= style->exclPrefixMax) {
-        style->exclPrefixMax *= 2;
-        style->exclPrefixTab =
-            (xmlChar * *)xmlRealloc(style->exclPrefixTab,
-                                    style->exclPrefixMax *
-                                    sizeof(style->exclPrefixTab[0]));
-        if (style->exclPrefixTab == NULL) {
+        xmlChar **tmp;
+        size_t max = style->exclPrefixMax ? style->exclPrefixMax * 2 : 4;
+
+        tmp = xmlRealloc(style->exclPrefixTab,
+                         max * sizeof(style->exclPrefixTab[0]));
+        if (tmp == NULL) {
             xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
             return (-1);
         }
+        style->exclPrefixTab = tmp;
+        style->exclPrefixMax = max;
     }
     style->exclPrefixTab[style->exclPrefixNr] = value;
     style->exclPrefix = value;
@@ -1111,9 +1103,9 @@
 	               xmlNodePtr node)
 {
     xmlNsPtr cur;
-    xmlNsPtr *ret = NULL;
+    xmlNsPtr *ret = NULL, *tmp;
     int nbns = 0;
-    int maxns = 10;
+    int maxns = 0;
     int i;
 
     if ((style == NULL) || (template == NULL) || (node == NULL) ||
@@ -1138,17 +1130,6 @@
 		    if (xmlStrEqual(cur->href, style->exclPrefixTab[i]))
 			goto skip_ns;
 		}
-                if (ret == NULL) {
-                    ret =
-                        (xmlNsPtr *) xmlMalloc((maxns + 1) *
-                                               sizeof(xmlNsPtr));
-                    if (ret == NULL) {
-                        xmlGenericError(xmlGenericErrorContext,
-                                        "xsltGetInheritedNsList : out of memory!\n");
-                        return(0);
-                    }
-                    ret[nbns] = NULL;
-                }
 		/*
 		* Skip shadowed namespace bindings.
 		*/
@@ -1159,16 +1140,16 @@
                 }
                 if (i >= nbns) {
                     if (nbns >= maxns) {
-                        maxns *= 2;
-                        ret = (xmlNsPtr *) xmlRealloc(ret,
-                                                      (maxns +
-                                                       1) *
-                                                      sizeof(xmlNsPtr));
-                        if (ret == NULL) {
+                        maxns = (maxns == 0) ? 10 : 2 * maxns;
+                        tmp = (xmlNsPtr *) xmlRealloc(ret,
+                                (maxns + 1) * sizeof(xmlNsPtr));
+                        if (tmp == NULL) {
                             xmlGenericError(xmlGenericErrorContext,
                                             "xsltGetInheritedNsList : realloc failed!\n");
+                            xmlFree(ret);
                             return(0);
                         }
+                        ret = tmp;
                     }
                     ret[nbns++] = cur;
                     ret[nbns] = NULL;
@@ -1324,8 +1305,10 @@
     if (elements != NULL) {
         if (style->cdataSection == NULL)
             style->cdataSection = xmlHashCreate(10);
-        if (style->cdataSection == NULL)
+        if (style->cdataSection == NULL) {
+            xmlFree(elements);
             return;
+        }
 
         element = elements;
         while (*element != 0) {
@@ -1563,8 +1546,10 @@
 
     if (style->stripSpaces == NULL)
 	style->stripSpaces = xmlHashCreate(10);
-    if (style->stripSpaces == NULL)
+    if (style->stripSpaces == NULL) {
+        xmlFree(elements);
 	return;
+    }
 
     element = elements;
     while (*element != 0) {
@@ -1692,6 +1677,11 @@
     if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
 	return;
 
+    if (style->stripSpaces == NULL)
+	style->stripSpaces = xmlHashCreate(10);
+    if (style->stripSpaces == NULL)
+	return;
+
     elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL);
     if (elements == NULL) {
 	xsltTransformError(NULL, style, cur,
@@ -1700,11 +1690,6 @@
 	return;
     }
 
-    if (style->stripSpaces == NULL)
-	style->stripSpaces = xmlHashCreate(10);
-    if (style->stripSpaces == NULL)
-	return;
-
     element = elements;
     while (*element != 0) {
 	while (IS_BLANK(*element)) element++;
@@ -6838,7 +6823,8 @@
 	    if (val == NULL)
 		return(NULL);
 	    if ((xmlStrcasecmp(val, BAD_CAST "text/xml")) &&
-		(xmlStrcasecmp(val, BAD_CAST "text/xsl"))) {
+		(xmlStrcasecmp(val, BAD_CAST "text/xsl")) &&
+		(xmlStrcasecmp(val, BAD_CAST "application/xslt+xml"))) {
                 xmlFree(val);
 		break;
 	    }
diff --git a/third_party/libxslt/src/libxslt/xsltInternals.h b/third_party/libxslt/src/libxslt/xsltInternals.h
index 2fd1f68..7de638e 100644
--- a/third_party/libxslt/src/libxslt/xsltInternals.h
+++ b/third_party/libxslt/src/libxslt/xsltInternals.h
@@ -21,7 +21,6 @@
 #include <libxml/xmlstring.h>
 #include <libxslt/xslt.h>
 #include "xsltexports.h"
-#include "xsltlocale.h"
 #include "numbersInternals.h"
 
 #ifdef __cplusplus
@@ -1047,7 +1046,6 @@
     int      descending;	/* sort */
     const xmlChar *lang;	/* sort */
     int      has_lang;		/* sort */
-    xsltLocale locale;		/* sort */
     const xmlChar *case_order;	/* sort */
     int      lower_first;	/* sort */
 
@@ -1377,7 +1375,6 @@
     int      descending;	/* sort */
     const xmlChar *lang;	/* sort */
     int      has_lang;		/* sort */
-    xsltLocale locale;		/* sort */
     const xmlChar *case_order;	/* sort */
     int      lower_first;	/* sort */
 
@@ -1663,6 +1660,13 @@
     XSLT_OUTPUT_TEXT
 } xsltOutputType;
 
+typedef void *
+(*xsltNewLocaleFunc)(const xmlChar *lang, int lowerFirst);
+typedef void
+(*xsltFreeLocaleFunc)(void *locale);
+typedef xmlChar *
+(*xsltGenSortKeyFunc)(void *locale, const xmlChar *lang);
+
 typedef enum {
     XSLT_STATE_OK = 0,
     XSLT_STATE_ERROR,
@@ -1788,6 +1792,10 @@
     unsigned long opCount;
     int sourceDocDirty;
     unsigned long currentId; /* For generate-id() */
+
+    xsltNewLocaleFunc newLocale;
+    xsltFreeLocaleFunc freeLocale;
+    xsltGenSortKeyFunc genSortKey;
 };
 
 /**
diff --git a/third_party/libxslt/src/libxslt/xsltlocale.c b/third_party/libxslt/src/libxslt/xsltlocale.c
index 7674098..9324f284 100644
--- a/third_party/libxslt/src/libxslt/xsltlocale.c
+++ b/third_party/libxslt/src/libxslt/xsltlocale.c
@@ -19,6 +19,8 @@
 #include "xsltlocale.h"
 #include "xsltutils.h"
 
+#define XSLT_LOCALE_NONE
+
 #define TOUPPER(c) (c & ~0x20)
 #define TOLOWER(c) (c | 0x20)
 #define ISALPHA(c) ((unsigned)(TOUPPER(c) - 'A') < 26)
@@ -37,8 +39,7 @@
 struct xsltRFC1766Info_s {
       /*note typedef unsigned char xmlChar !*/
     xmlChar    tag[XSLTMAX_LANGTAGLEN+1];
-      /*note typedef LCID xsltLocale !*/
-    xsltLocale lcid;
+    LCID       lcid;
 };
 typedef struct xsltRFC1766Info_s xsltRFC1766Info;
 
@@ -46,14 +47,15 @@
 static xsltRFC1766Info *xsltLocaleList = NULL;
 
 
-static xsltLocale
+static void *
 xslt_locale_WINAPI(const xmlChar *languageTag) {
     int k;
     xsltRFC1766Info *p = xsltLocaleList;
 
     for (k=0; k<xsltLocaleListSize; k++, p++)
-	if (xmlStrcmp(p->tag, languageTag) == 0) return p->lcid;
-    return((xsltLocale)0);
+	if (xmlStrcmp(p->tag, languageTag) == 0)
+            return(&p->lcid);
+    return(NULL);
 }
 
 static void xsltEnumSupportedLocales(void);
@@ -83,17 +85,17 @@
  *
  * Returns the locale or NULL on error or if no matching locale was found
  */
-xsltLocale
-xsltNewLocale(const xmlChar *languageTag) {
+void *
+xsltNewLocale(const xmlChar *languageTag, int lowerFirst ATTRIBUTE_UNUSED) {
 #ifdef XSLT_LOCALE_POSIX
-    xsltLocale locale;
-    char localeName[XSLTMAX_LANGTAGLEN+6]; /* 6 chars for ".utf8\0" */
+    locale_t locale;
+    char localeName[XSLTMAX_LANGTAGLEN+7]; /* 7 chars for ".UTF-8\0" */
     const xmlChar *p = languageTag;
     const char *region = NULL;
     char *q = localeName;
     int i, llen;
 
-    /* Convert something like "pt-br" to "pt_BR.utf8" */
+    /* Convert something like "pt-br" to "pt_BR.UTF-8" */
 
     if (languageTag == NULL)
 	return(NULL);
@@ -117,7 +119,7 @@
 	if (i == 0 || *p)
 	    return(NULL);
 
-        memcpy(q, ".utf8", 6);
+        memcpy(q, ".UTF-8", 7);
         locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
         if (locale != NULL)
             return(locale);
@@ -129,7 +131,7 @@
 
     /* Try locale without territory, e.g. for Esperanto (eo) */
 
-    memcpy(q, ".utf8", 6);
+    memcpy(q, ".UTF-8", 7);
     locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
     if (locale != NULL)
         return(locale);
@@ -147,7 +149,7 @@
     *q++ = '_';
     *q++ = region[0];
     *q++ = region[1];
-    memcpy(q, ".utf8", 6);
+    memcpy(q, ".UTF-8", 7);
     locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
 
     return(locale);
@@ -155,7 +157,7 @@
 
 #ifdef XSLT_LOCALE_WINAPI
 {
-    xsltLocale    locale = (xsltLocale)0;
+    void          *locale = NULL;
     xmlChar       localeName[XSLTMAX_LANGTAGLEN+1];
     xmlChar       *q = localeName;
     const xmlChar *p = languageTag;
@@ -344,7 +346,7 @@
  * Frees a locale created with xsltNewLocale
  */
 void
-xsltFreeLocale(xsltLocale locale) {
+xsltFreeLocale(void *locale) {
 #ifdef XSLT_LOCALE_POSIX
     if (locale != NULL)
         freelocale(locale);
@@ -358,58 +360,82 @@
  * @locale: locale created with xsltNewLocale
  * @string: UTF-8 string to transform
  *
- * Transforms a string according to locale. The transformed string must then be
- * compared with xsltLocaleStrcmp and freed with xmlFree.
+ * Transforms a string according to locale. The transformed string must be
+ * freed with xmlFree.
  *
  * Returns the transformed string or NULL on error
  */
-xsltLocaleChar *
-xsltStrxfrm(xsltLocale locale, const xmlChar *string)
+xmlChar *
+xsltStrxfrm(void *vlocale, const xmlChar *string)
 {
 #ifdef XSLT_LOCALE_NONE
     return(NULL);
 #else
-    size_t xstrlen, r;
-    xsltLocaleChar *xstr;
+    xmlChar *xstr;
 
 #ifdef XSLT_LOCALE_POSIX
-    xstrlen = strxfrm_l(NULL, (const char *)string, 0, locale) + 1;
-    xstr = (xsltLocaleChar *) xmlMalloc(xstrlen);
+    size_t xstrlen, r;
+
+    xstrlen = strxfrm_l(NULL, (const char *)string, 0, vlocale) + 1;
+    xstr = (xmlChar *) xmlMalloc(xstrlen);
     if (xstr == NULL) {
 	xsltTransformError(NULL, NULL, NULL,
 	    "xsltStrxfrm : out of memory error\n");
 	return(NULL);
     }
 
-    r = strxfrm_l((char *)xstr, (const char *)string, xstrlen, locale);
-#endif
-
-#ifdef XSLT_LOCALE_WINAPI
-    (void) locale;
-    xstrlen = MultiByteToWideChar(CP_UTF8, 0, (char *) string, -1, NULL, 0);
-    if (xstrlen == 0) {
-        xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar check failed\n");
-        return(NULL);
-    }
-    xstr = (xsltLocaleChar*) xmlMalloc(xstrlen * sizeof(xsltLocaleChar));
-    if (xstr == NULL) {
-        xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n");
-        return(NULL);
-    }
-    r = MultiByteToWideChar(CP_UTF8, 0, (char *) string, -1, xstr, xstrlen);
-    if (r == 0) {
-        xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar failed\n");
-        xmlFree(xstr);
-        return(NULL);
-    }
-    return(xstr);
-#endif /* XSLT_LOCALE_WINAPI */
+    r = strxfrm_l((char *)xstr, (const char *)string, xstrlen, vlocale);
 
     if (r >= xstrlen) {
 	xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : strxfrm failed\n");
         xmlFree(xstr);
         return(NULL);
     }
+#endif
+
+#ifdef XSLT_LOCALE_WINAPI
+    int wstrlen, xstrlen, r;
+    wchar_t *wstr;
+    LCID *lcid = vlocale;
+
+    wstrlen = MultiByteToWideChar(CP_UTF8, 0, (char *) string, -1, NULL, 0);
+    if (wstrlen == 0) {
+        xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar check failed\n");
+        return(NULL);
+    }
+    wstr = (wchar_t *) xmlMalloc(wstrlen * sizeof(wchar_t));
+    if (wstr == NULL) {
+        xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n");
+        return(NULL);
+    }
+    r = MultiByteToWideChar(CP_UTF8, 0, (char *) string, -1, wstr, wstrlen);
+    if (r == 0) {
+        xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar failed\n");
+        xmlFree(wstr);
+        return(NULL);
+    }
+    /* This returns the size in bytes. */
+    xstrlen = LCMapStringW(*lcid, LCMAP_SORTKEY, wstr, wstrlen, NULL, 0);
+    if (xstrlen == 0) {
+        xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : LCMapStringW failed\n");
+        xmlFree(wstr);
+        return(NULL);
+    }
+    xstr = (xmlChar*) xmlMalloc(xstrlen);
+    if (xstr == NULL) {
+        xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n");
+        xmlFree(wstr);
+        return(NULL);
+    }
+    r = LCMapStringW(*lcid, LCMAP_SORTKEY, wstr, wstrlen, (wchar_t *) xstr,
+                     xstrlen);
+    xmlFree(wstr);
+    if (r == 0) {
+        xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : LCMapStringW failed\n");
+        xmlFree(xstr);
+        return(NULL);
+    }
+#endif /* XSLT_LOCALE_WINAPI */
 
     return(xstr);
 #endif /* XSLT_LOCALE_NONE */
@@ -417,35 +443,22 @@
 
 /**
  * xsltLocaleStrcmp:
- * @locale: a locale identifier
+ * @locale: unused
  * @str1: a string transformed with xsltStrxfrm
  * @str2: a string transformed with xsltStrxfrm
  *
- * Compares two strings transformed with xsltStrxfrm
+ * DEPRECATED: Same as xmlStrcmp.
+ *
+ * Compares two strings transformed with xsltStrxfrm.
  *
  * Returns a value < 0 if str1 sorts before str2,
  *         a value > 0 if str1 sorts after str2,
  *         0 if str1 and str2 are equal wrt sorting
  */
 int
-xsltLocaleStrcmp(xsltLocale locale, const xsltLocaleChar *str1, const xsltLocaleChar *str2) {
+xsltLocaleStrcmp(void *locale, const xmlChar *str1, const xmlChar *str2) {
     (void)locale;
-#ifdef XSLT_LOCALE_WINAPI
-{
-    int ret;
-    if (str1 == str2) return(0);
-    if (str1 == NULL) return(-1);
-    if (str2 == NULL) return(1);
-    ret = CompareStringW(locale, 0, str1, -1, str2, -1);
-    if (ret == 0) {
-        xsltTransformError(NULL, NULL, NULL, "xsltLocaleStrcmp : CompareStringW fail\n");
-        return(0);
-    }
-    return(ret - 2);
-}
-#else
     return(xmlStrcmp(str1, str2));
-#endif
 }
 
 #ifdef XSLT_LOCALE_WINAPI
diff --git a/third_party/libxslt/src/libxslt/xsltlocale.h b/third_party/libxslt/src/libxslt/xsltlocale.h
index db881e2b..10a4402 100644
--- a/third_party/libxslt/src/libxslt/xsltlocale.h
+++ b/third_party/libxslt/src/libxslt/xsltlocale.h
@@ -14,65 +14,23 @@
 #include <libxml/xmlstring.h>
 #include "xsltexports.h"
 
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#include <windows.h>
-#include <winnls.h>
-#endif
-
-#if 0
-
-/*
- * XSLT_LOCALE_POSIX:
- * Macro indicating to use POSIX locale extensions
- */
-#define XSLT_LOCALE_POSIX
-
-#ifdef HAVE_LOCALE_H
-#include <locale.h>
-#endif
-#ifdef HAVE_XLOCALE_H
-#include <xlocale.h>
-#endif
-
-typedef locale_t xsltLocale;
-typedef xmlChar xsltLocaleChar;
-
-#elif 0
-
-/*
- * XSLT_LOCALE_WINAPI:
- * Macro indicating to use WinAPI for extended locale support
- */
-#define XSLT_LOCALE_WINAPI
-
-typedef LCID xsltLocale;
-typedef wchar_t xsltLocaleChar;
-
-#else
-
-/*
- * XSLT_LOCALE_NONE:
- * Macro indicating that there's no extended locale support
- */
-#define XSLT_LOCALE_NONE
-
-typedef void *xsltLocale;
-typedef xmlChar xsltLocaleChar;
-
-#endif
-
-XSLTPUBFUN xsltLocale XSLTCALL
-	xsltNewLocale			(const xmlChar *langName);
+XSLTPUBFUN void * XSLTCALL
+	xsltNewLocale			(const xmlChar *langName,
+					 int lowerFirst);
 XSLTPUBFUN void XSLTCALL
-	xsltFreeLocale			(xsltLocale locale);
-XSLTPUBFUN xsltLocaleChar * XSLTCALL
-	xsltStrxfrm			(xsltLocale locale,
+	xsltFreeLocale			(void *locale);
+XSLTPUBFUN xmlChar * XSLTCALL
+	xsltStrxfrm			(void *locale,
 					 const xmlChar *string);
-XSLTPUBFUN int XSLTCALL
-	xsltLocaleStrcmp		(xsltLocale locale,
-					 const xsltLocaleChar *str1,
-					 const xsltLocaleChar *str2);
 XSLTPUBFUN void XSLTCALL
 	xsltFreeLocales			(void);
 
+/* Backward compatibility */
+typedef void *xsltLocale;
+typedef xmlChar xsltLocaleChar;
+XSLTPUBFUN int XSLTCALL
+	xsltLocaleStrcmp		(void *locale,
+					 const xmlChar *str1,
+					 const xmlChar *str2);
+
 #endif /* __XML_XSLTLOCALE_H__ */
diff --git a/third_party/libxslt/src/libxslt/xsltutils.c b/third_party/libxslt/src/libxslt/xsltutils.c
index 390b958..59dd192d 100644
--- a/third_party/libxslt/src/libxslt/xsltutils.c
+++ b/third_party/libxslt/src/libxslt/xsltutils.c
@@ -42,6 +42,7 @@
 #include "transform.h"
 
 #if defined(_WIN32)
+#include <windows.h>
 #define XSLT_WIN32_PERFORMANCE_COUNTER
 #endif
 
@@ -960,7 +961,7 @@
  */
 static xmlXPathObjectPtr *
 xsltComputeSortResultInternal(xsltTransformContextPtr ctxt, xmlNodePtr sort,
-                              int number, xsltLocale locale) {
+                              int number, void *locale) {
 #ifdef XSLT_REFACTORED
     xsltStyleItemSortPtr comp;
 #else
@@ -1034,6 +1035,8 @@
 		res = xmlXPathConvertString(res);
 	    if (number)
 		res = xmlXPathConvertNumber(res);
+        }
+        if (res != NULL) {
 	    res->index = i;	/* Save original pos for dupl resolv */
 	    if (number) {
 		if (res->type == XPATH_NUMBER) {
@@ -1047,10 +1050,17 @@
 		}
 	    } else {
 		if (res->type == XPATH_STRING) {
-		    if (locale != (xsltLocale)0) {
+		    if (locale != NULL) {
 			xmlChar *str = res->stringval;
-			res->stringval = (xmlChar *) xsltStrxfrm(locale, str);
-			xmlFree(str);
+                        xmlChar *sortKey = ctxt->genSortKey(locale, str);
+
+                        if (sortKey == NULL) {
+                            xsltTransformError(ctxt, NULL, sort,
+                                "xsltComputeSortResult: sort key is null\n");
+                        } else {
+                            res->stringval = sortKey;
+                            xmlFree(str);
+                        }
 		    }
 
 		    results[i] = res;
@@ -1094,7 +1104,8 @@
 
     if (comp != NULL)
         number = comp->number;
-    return xsltComputeSortResultInternal(ctxt, sort, number, /* locale */ 0);
+    return xsltComputeSortResultInternal(ctxt, sort, number,
+                                         /* locale */ NULL);
 }
 
 /**
@@ -1124,7 +1135,7 @@
     xmlNodePtr node;
     xmlXPathObjectPtr tmp;
     int number[XSLT_MAX_SORT], desc[XSLT_MAX_SORT];
-    xsltLocale locale[XSLT_MAX_SORT];
+    void *locale[XSLT_MAX_SORT];
 
     if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
 	(nbsorts >= XSLT_MAX_SORT))
@@ -1140,6 +1151,8 @@
 	return; /* nothing to do */
 
     for (j = 0; j < nbsorts; j++) {
+        xmlChar *lang;
+
 	comp = sorts[j]->psvi;
 	if ((comp->stype == NULL) && (comp->has_stype != 0)) {
 	    xmlChar *stype =
@@ -1181,17 +1194,18 @@
 	    desc[j] = comp->descending;
 	}
 	if ((comp->lang == NULL) && (comp->has_lang != 0)) {
-            xmlChar *lang = xsltEvalAttrValueTemplate(ctxt, sorts[j],
+            lang = xsltEvalAttrValueTemplate(ctxt, sorts[j],
 						      (xmlChar *) "lang",
 						      NULL);
-	    if (lang != NULL) {
-                locale[j] = xsltNewLocale(lang);
-                xmlFree(lang);
-            } else {
-                locale[j] = 0;
-            }
 	} else {
-            locale[j] = comp->locale;
+            lang = (xmlChar *) comp->lang;
+        }
+        if (lang != NULL) {
+            locale[j] = ctxt->newLocale(lang, comp->lower_first);
+            if (lang != comp->lang)
+                xmlFree(lang);
+        } else {
+            locale[j] = NULL;
         }
     }
 
@@ -1236,11 +1250,6 @@
 				results[j + incr]->floatval)
 			    tst = 1;
 			else tst = -1;
-		    } else if(locale[0] != (xsltLocale)0) {
-			tst = xsltLocaleStrcmp(
-			    locale[0],
-			    (xsltLocaleChar *) results[j]->stringval,
-			    (xsltLocaleChar *) results[j + incr]->stringval);
 		    } else {
 			tst = xmlStrcmp(results[j]->stringval,
 				     results[j + incr]->stringval);
@@ -1298,11 +1307,6 @@
 					res[j + incr]->floatval)
 				    tst = 1;
 				else tst = -1;
-			    } else if(locale[depth] != (xsltLocale)0) {
-				tst = xsltLocaleStrcmp(
-				    locale[depth],
-				    (xsltLocaleChar *) res[j]->stringval,
-				    (xsltLocaleChar *) res[j + incr]->stringval);
 			    } else {
 				tst = xmlStrcmp(res[j]->stringval,
 					     res[j + incr]->stringval);
@@ -1351,11 +1355,8 @@
 
 cleanup:
     for (j = 0; j < nbsorts; j++) {
-	comp = sorts[j]->psvi;
-	if ((comp->lang == NULL) && (comp->has_lang != 0)) {
-            if (locale[j] != (xsltLocale)0) {
-                xsltFreeLocale(locale[j]);
-            }
+        if (locale[j] != NULL) {
+            ctxt->freeLocale(locale[j]);
         }
 	if (resultsTab[j] != NULL) {
 	    for (i = 0;i < len;i++)
@@ -1396,6 +1397,8 @@
  * xsltSetSortFunc:
  * @handler:  the new handler function
  *
+ * DEPRECATED: Use xsltSetCtxtLocaleHandlers.
+ *
  * Function to reset the global handler for XSLT sorting.
  * If the handler is NULL, the default sort function will be used.
  */
@@ -1412,6 +1415,8 @@
  * @ctxt:  a XSLT process context
  * @handler:  the new handler function
  *
+ * DEPRECATED: Use xsltSetCtxtLocaleHandlers.
+ *
  * Function to set the handler for XSLT sorting
  * for the specified context.
  * If the handler is NULL, then the global
@@ -1422,6 +1427,28 @@
     ctxt->sortfunc = handler;
 }
 
+/**
+ * xsltSetCtxtLocaleHandlers:
+ * @ctxt:  an XSLT transform context
+ * @newLocale:  locale constructor
+ * @freeLocale:  locale destructor
+ * @genSortKey  sort key generator
+ *
+ * Set the locale handlers.
+ */
+void
+xsltSetCtxtLocaleHandlers(xsltTransformContextPtr ctxt,
+                          xsltNewLocaleFunc newLocale,
+                          xsltFreeLocaleFunc freeLocale,
+                          xsltGenSortKeyFunc genSortKey) {
+    if (ctxt == NULL)
+        return;
+
+    ctxt->newLocale = newLocale;
+    ctxt->freeLocale = freeLocale;
+    ctxt->genSortKey = genSortKey;
+}
+
 /************************************************************************
  *									*
  *				Parsing options				*
@@ -1801,6 +1828,8 @@
 			 (const xmlChar *) "UTF-8")))
 	    encoder = NULL;
 	buf = xmlAllocOutputBuffer(encoder);
+        if (buf == NULL)
+            xmlCharEncCloseFunc(encoder);
     } else {
 	buf = xmlAllocOutputBuffer(NULL);
     }
diff --git a/third_party/libxslt/src/libxslt/xsltutils.h b/third_party/libxslt/src/libxslt/xsltutils.h
index 484032e..bca59dd 100644
--- a/third_party/libxslt/src/libxslt/xsltutils.h
+++ b/third_party/libxslt/src/libxslt/xsltutils.h
@@ -179,6 +179,11 @@
 		xsltSetCtxtSortFunc		(xsltTransformContextPtr ctxt,
 						 xsltSortFunc handler);
 XSLTPUBFUN void XSLTCALL
+		xsltSetCtxtLocaleHandlers	(xsltTransformContextPtr ctxt,
+						 xsltNewLocaleFunc newLocale,
+						 xsltFreeLocaleFunc freeLocale,
+						 xsltGenSortKeyFunc genSortKey);
+XSLTPUBFUN void XSLTCALL
 		xsltDefaultSortFunction		(xsltTransformContextPtr ctxt,
 						 xmlNodePtr *sorts,
 						 int nbsorts);
diff --git a/tools/autotest.py b/tools/autotest.py
index b5093cfe..9c3e8a5 100755
--- a/tools/autotest.py
+++ b/tools/autotest.py
@@ -245,11 +245,14 @@
 
   test_files = exact if len(exact) > 0 else close
   if len(test_files) > 1:
-    # Arbitrarily capping at 10 results so we don't print the name of every file
-    # in the repo if the target is poorly specified.
-    test_files = test_files[:10]
-    ExitWithMessage(f'Target "{target}" is ambiguous. Matching files: '
-                    f'{test_files}')
+    if len(test_files) < 10:
+      test_files = [HaveUserPickFile(test_files)]
+    else:
+      # Arbitrarily capping at 10 results so we don't print the name of every
+      # file in the repo if the target is poorly specified.
+      test_files = test_files[:10]
+      ExitWithMessage(f'Target "{target}" is ambiguous. Matching files: '
+                      f'{test_files}')
   if not test_files:
     ExitWithMessage(f'Target "{target}" did not match any files.')
   return test_files
@@ -261,6 +264,19 @@
   return target in _OTHER_TEST_TARGETS
 
 
+def HaveUserPickFile(paths):
+  paths = sorted(paths, key=lambda p: (len(p), p))
+  path_list = '\n'.join(f'{i}. {t}' for i, t in enumerate(paths))
+
+  while True:
+    user_input = input(f'Please choose the path you mean.\n{path_list}\n')
+    try:
+      value = int(user_input)
+      return paths[value]
+    except (ValueError, IndexError):
+      print('Try again')
+
+
 def HaveUserPickTarget(paths, targets):
   # Cap to 10 targets for convenience [0-9].
   targets = targets[:10]
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index 03c4a6a..65cb5b2 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -740,10 +740,6 @@
     "META": {"sizes": {"includes": [50],}},
     "includes": [3405],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/projector_app/resources/annotator/trusted/ash_projector_annotator_trusted_resources.grd": {
-    "META": {"sizes": {"includes": [50],}},
-    "includes": [3410],
-  },
   "<(SHARED_INTERMEDIATE_DIR)/ash/webui/projector_app/resources/common/ash_projector_common_resources.grd": {
     "META": {"sizes": {"includes": [50],}},
     "includes": [3415],
diff --git a/tools/memory/BUILD.gn b/tools/memory/BUILD.gn
new file mode 100644
index 0000000..a7945854
--- /dev/null
+++ b/tools/memory/BUILD.gn
@@ -0,0 +1,13 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+group("all") {
+  testonly = true
+  deps = [
+    "partition_allocator:all",
+    "simulator:all",
+  ]
+}
diff --git a/tools/memory/simulator/BUILD.gn b/tools/memory/simulator/BUILD.gn
new file mode 100644
index 0000000..ac1cf20
--- /dev/null
+++ b/tools/memory/simulator/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+group("all") {
+  deps = [ ":memory_simulator" ]
+}
+
+static_library("memory_simulator_lib") {
+  sources = [
+    "contiguous_memory_holder.cc",
+    "contiguous_memory_holder.h",
+    "memory_holder.cc",
+    "memory_holder.h",
+    "memory_simulator.cc",
+    "memory_simulator.h",
+    "metrics_printer.cc",
+    "metrics_printer.h",
+    "metrics_provider.cc",
+    "metrics_provider.h",
+    "simulator_metrics_provider.cc",
+    "simulator_metrics_provider.h",
+    "utils.cc",
+    "utils.h",
+  ]
+
+  if (is_mac) {
+    sources += [
+      "process_metrics_provider_mac.cc",
+      "process_metrics_provider_mac.h",
+      "system_metrics_provider_mac.cc",
+      "system_metrics_provider_mac.h",
+    ]
+  }
+
+  deps = [ "//base" ]
+}
+
+executable("memory_simulator") {
+  sources = [ "memory_simulator_main.cc" ]
+  deps = [
+    ":memory_simulator_lib",
+    "//base",
+  ]
+}
diff --git a/tools/memory/simulator/OWNERS b/tools/memory/simulator/OWNERS
new file mode 100644
index 0000000..1fb7e613
--- /dev/null
+++ b/tools/memory/simulator/OWNERS
@@ -0,0 +1,2 @@
+fdoray@chromium.org
+lizeb@chromium.org
diff --git a/tools/memory/simulator/contiguous_memory_holder.cc b/tools/memory/simulator/contiguous_memory_holder.cc
new file mode 100644
index 0000000..846011a
--- /dev/null
+++ b/tools/memory/simulator/contiguous_memory_holder.cc
@@ -0,0 +1,94 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/memory/simulator/contiguous_memory_holder.h"
+
+#include <cstdint>
+
+#include "base/allocator/partition_allocator/page_allocator.h"
+#include "base/check_op.h"
+#include "base/debug/alias.h"
+#include "base/memory/page_size.h"
+
+namespace memory_simulator {
+
+ContiguousMemoryHolder::ContiguousMemoryHolder(size_t max_pages)
+    : memory_length_(max_pages * base::GetPageSize()),
+      memory_(partition_alloc::AllocPages(
+          memory_length_,
+          base::GetPageSize(),
+          ::partition_alloc::PageAccessibilityConfiguration(
+              partition_alloc::PageAccessibilityConfiguration::kReadWrite),
+          partition_alloc::PageTag::kSimulation)),
+      alloc_position_(reinterpret_cast<uint64_t*>(memory_)),
+      read_position_(reinterpret_cast<uint64_t*>(memory_)),
+      write_position_(reinterpret_cast<uint64_t*>(memory_)) {
+  CHECK_NE(0u, memory_);
+}
+
+ContiguousMemoryHolder::~ContiguousMemoryHolder() {
+  partition_alloc::FreePages(memory_, memory_length_);
+}
+
+void ContiguousMemoryHolder::Allocate() {
+  DCHECK_EQ((reinterpret_cast<uintptr_t>(alloc_position_) - memory_) %
+                base::GetPageSize(),
+            0u);
+  DCHECK_LT(reinterpret_cast<uintptr_t>(alloc_position_),
+            memory_ + memory_length_);
+
+  // Writing zeros to the memory page forces it to be added to this process'
+  // private footprint.
+  for (size_t i = 0; i < base::GetPageSize() / sizeof(uint64_t); ++i) {
+    *alloc_position_ = 0;
+    ++alloc_position_;
+  }
+}
+
+void ContiguousMemoryHolder::Read() {
+  if (read_position_ >= alloc_position_) {
+    read_position_ = reinterpret_cast<uint64_t*>(memory_);
+    if (read_position_ >= alloc_position_) {
+      // No-op if the initial allocation didn't happen.
+      return;
+    }
+  }
+
+  DCHECK_LT(reinterpret_cast<uintptr_t>(read_position_),
+            memory_ + memory_length_);
+  DCHECK_EQ((reinterpret_cast<uintptr_t>(read_position_) - memory_) %
+                base::GetPageSize(),
+            0u);
+
+  uint64_t sum = 0;
+  for (size_t i = 0; i < base::GetPageSize() / sizeof(uint64_t); ++i) {
+    sum += *read_position_;
+    ++read_position_;
+  }
+
+  base::debug::Alias(&sum);
+}
+
+void ContiguousMemoryHolder::Write() {
+  if (write_position_ >= alloc_position_) {
+    write_position_ = reinterpret_cast<uint64_t*>(memory_);
+    if (write_position_ >= alloc_position_) {
+      // No-op if the initial allocation didn't happen.
+      return;
+    }
+  }
+
+  DCHECK_LT(reinterpret_cast<uintptr_t>(write_position_),
+            memory_ + memory_length_);
+  DCHECK_EQ((reinterpret_cast<uintptr_t>(write_position_) - memory_) %
+                base::GetPageSize(),
+            0u);
+
+  for (size_t i = 0; i < base::GetPageSize() / sizeof(uint64_t); ++i) {
+    *write_position_ = Rand();
+    ++write_position_;
+  }
+}
+
+}  // namespace memory_simulator
diff --git a/tools/memory/simulator/contiguous_memory_holder.h b/tools/memory/simulator/contiguous_memory_holder.h
new file mode 100644
index 0000000..2aab4b8
--- /dev/null
+++ b/tools/memory/simulator/contiguous_memory_holder.h
@@ -0,0 +1,39 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_MEMORY_SIMULATOR_CONTIGUOUS_MEMORY_HOLDER_H_
+#define TOOLS_MEMORY_SIMULATOR_CONTIGUOUS_MEMORY_HOLDER_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "tools/memory/simulator/memory_holder.h"
+
+namespace memory_simulator {
+
+// A class that holds memory pages at contiguous virtual addresses.
+class ContiguousMemoryHolder : public MemoryHolder {
+ public:
+  // `max_pages` is the maximum number of pages that can be allocated through
+  // this object (exceeding it will result in a crash).
+  explicit ContiguousMemoryHolder(size_t max_pages);
+  ~ContiguousMemoryHolder() override;
+
+  // MemoryHolder:
+  void Allocate() override;
+  void Read() override;
+  void Write() override;
+
+ private:
+  const size_t memory_length_;
+  const uintptr_t memory_;
+
+  uint64_t* alloc_position_ = nullptr;
+  uint64_t* read_position_ = nullptr;
+  uint64_t* write_position_ = nullptr;
+};
+
+}  // namespace memory_simulator
+
+#endif  // TOOLS_MEMORY_SIMULATOR_CONTIGUOUS_MEMORY_HOLDER_H_
diff --git a/tools/memory/simulator/memory_holder.cc b/tools/memory/simulator/memory_holder.cc
new file mode 100644
index 0000000..a76c2bdd
--- /dev/null
+++ b/tools/memory/simulator/memory_holder.cc
@@ -0,0 +1,16 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/memory/simulator/memory_holder.h"
+
+namespace memory_simulator {
+
+MemoryHolder::MemoryHolder() = default;
+MemoryHolder::~MemoryHolder() = default;
+
+uint64_t MemoryHolder::Rand() {
+  return random_generator_.RandUint64();
+}
+
+}  // namespace memory_simulator
diff --git a/tools/memory/simulator/memory_holder.h b/tools/memory/simulator/memory_holder.h
new file mode 100644
index 0000000..0a42afb
--- /dev/null
+++ b/tools/memory/simulator/memory_holder.h
@@ -0,0 +1,44 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_MEMORY_SIMULATOR_MEMORY_HOLDER_H_
+#define TOOLS_MEMORY_SIMULATOR_MEMORY_HOLDER_H_
+
+#include <stdint.h>
+
+#include "base/rand_util.h"
+
+namespace memory_simulator {
+
+// Interface for a class that supports these operations:
+// - Allocate a new memory page
+// - Read the next memory page
+// - Write the next memory page
+class MemoryHolder {
+ public:
+  MemoryHolder();
+  virtual ~MemoryHolder();
+
+  // Add a page filled with zeros to the "private footprint" of this process.
+  virtual void Allocate() = 0;
+
+  // Read the next memory page. When the end is reached, goes back to the first
+  // page. No-ops if no page was allocated yet.
+  virtual void Read() = 0;
+
+  // Writes to the next memory page. When the end is reached, goes back to the
+  // first page. No-ops if no page was allocated yet.
+  virtual void Write() = 0;
+
+ protected:
+  // Returns a random number for use by Write() implementations.
+  uint64_t Rand();
+
+ private:
+  base::InsecureRandomGenerator random_generator_;
+};
+
+}  // namespace memory_simulator
+
+#endif  // TOOLS_MEMORY_SIMULATOR_MEMORY_HOLDER_H_
diff --git a/tools/memory/simulator/memory_simulator.cc b/tools/memory/simulator/memory_simulator.cc
new file mode 100644
index 0000000..9281099
--- /dev/null
+++ b/tools/memory/simulator/memory_simulator.cc
@@ -0,0 +1,108 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/memory/simulator/memory_simulator.h"
+
+#include <cstdint>
+
+#include "base/check.h"
+#include "base/debug/alias.h"
+#include "base/logging.h"
+#include "base/memory/page_size.h"
+#include "base/time/time.h"
+#include "tools/memory/simulator/contiguous_memory_holder.h"
+
+namespace memory_simulator {
+
+MemorySimulator::MemorySimulator() = default;
+MemorySimulator::~MemorySimulator() = default;
+
+void MemorySimulator::Start(int64_t page_alloc_per_sec,
+                            int64_t page_read_per_sec,
+                            int64_t page_write_per_sec,
+                            int64_t max_pages_allocated,
+                            base::TimeTicks read_deadline,
+                            base::TimeTicks write_deadline) {
+  memory_holder_ =
+      std::make_unique<ContiguousMemoryHolder>(max_pages_allocated);
+  max_pages_allocated_ = max_pages_allocated;
+
+  if (page_alloc_per_sec != 0) {
+    alloc_timer_.Start(FROM_HERE, base::Hertz(page_alloc_per_sec), this,
+                       &MemorySimulator::Allocate);
+  }
+
+  if (page_read_per_sec != 0) {
+    read_timer_.Start(FROM_HERE, base::Hertz(page_read_per_sec), this,
+                      &MemorySimulator::Read);
+    read_deadline_ = read_deadline;
+  }
+
+  if (page_write_per_sec != 0) {
+    write_timer_.Start(FROM_HERE, base::Hertz(page_write_per_sec), this,
+                       &MemorySimulator::Write);
+    write_deadline_ = write_deadline;
+  }
+}
+
+void MemorySimulator::StopAndFree() {
+  memory_holder_.reset();
+  alloc_timer_.Stop();
+  read_timer_.Stop();
+  write_timer_.Stop();
+  pages_allocated_ = 0;
+}
+
+int64_t MemorySimulator::GetPagesAllocated() const {
+  return pages_allocated_;
+}
+
+int64_t MemorySimulator::GetPagesRead() const {
+  return pages_read_;
+}
+
+int64_t MemorySimulator::GetPagesWritten() const {
+  return pages_written_;
+}
+
+void MemorySimulator::Allocate() {
+  CHECK_LE(pages_allocated_, max_pages_allocated_);
+  if (pages_allocated_ == max_pages_allocated_) {
+    alloc_timer_.Stop();
+    return;
+  }
+
+  memory_holder_->Allocate();
+  ++pages_allocated_;
+}
+
+void MemorySimulator::Read() {
+  if (base::TimeTicks::Now() >= read_deadline_) {
+    read_timer_.Stop();
+    return;
+  }
+
+  if (pages_allocated_ == 0) {
+    return;
+  }
+
+  memory_holder_->Read();
+  ++pages_read_;
+}
+
+void MemorySimulator::Write() {
+  if (base::TimeTicks::Now() >= write_deadline_) {
+    write_timer_.Stop();
+    return;
+  }
+
+  if (pages_allocated_ == 0) {
+    return;
+  }
+
+  memory_holder_->Write();
+  ++pages_written_;
+}
+
+}  // namespace memory_simulator
diff --git a/tools/memory/simulator/memory_simulator.h b/tools/memory/simulator/memory_simulator.h
new file mode 100644
index 0000000..594a26f4
--- /dev/null
+++ b/tools/memory/simulator/memory_simulator.h
@@ -0,0 +1,59 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_MEMORY_SIMULATOR_MEMORY_SIMULATOR_H_
+#define TOOLS_MEMORY_SIMULATOR_MEMORY_SIMULATOR_H_
+
+#include <stdint.h>
+#include <cstdint>
+#include <memory>
+
+#include "base/rand_util.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "tools/memory/simulator/memory_holder.h"
+
+namespace memory_simulator {
+
+// Allocates, reads and writes memory pages at a configurable frequency.
+class MemorySimulator {
+ public:
+  MemorySimulator();
+  ~MemorySimulator();
+
+  void Start(int64_t page_alloc_per_second,
+             int64_t page_read_per_second,
+             int64_t page_write_per_second,
+             int64_t max_pages_allocated,
+             base::TimeTicks read_deadline,
+             base::TimeTicks write_deadline);
+
+  void StopAndFree();
+
+  int64_t GetPagesAllocated() const;
+  int64_t GetPagesRead() const;
+  int64_t GetPagesWritten() const;
+
+ private:
+  void Allocate();
+  void Read();
+  void Write();
+
+  std::unique_ptr<MemoryHolder> memory_holder_;
+  int64_t pages_allocated_ = 0;
+  int64_t max_pages_allocated_ = 0;
+  int64_t pages_read_ = 0;
+  int64_t pages_written_ = 0;
+
+  base::MetronomeTimer alloc_timer_;
+  base::MetronomeTimer read_timer_;
+  base::MetronomeTimer write_timer_;
+
+  base::TimeTicks read_deadline_;
+  base::TimeTicks write_deadline_;
+};
+
+}  // namespace memory_simulator
+
+#endif  // TOOLS_MEMORY_SIMULATOR_MEMORY_SIMULATOR_H_
diff --git a/tools/memory/simulator/memory_simulator_main.cc b/tools/memory/simulator/memory_simulator_main.cc
new file mode 100644
index 0000000..63af6416
--- /dev/null
+++ b/tools/memory/simulator/memory_simulator_main.cc
@@ -0,0 +1,249 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+#include <iostream>
+#include <limits>
+
+#include "base/command_line.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback_forward.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/task/single_thread_task_executor.h"
+#include "base/timer/timer.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "tools/memory/simulator/memory_simulator.h"
+#include "tools/memory/simulator/metrics_printer.h"
+#include "tools/memory/simulator/simulator_metrics_provider.h"
+#include "tools/memory/simulator/utils.h"
+
+#if BUILDFLAG(IS_MAC)
+#include "tools/memory/simulator/process_metrics_provider_mac.h"
+#include "tools/memory/simulator/system_metrics_provider_mac.h"
+#endif  // BUILDFLAG(IS_MAC)
+
+namespace {
+
+void InitLogging() {
+  logging::LoggingSettings settings;
+  settings.logging_dest =
+      logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
+  settings.log_file_path = nullptr;
+  settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+  settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
+  bool logging_res = logging::InitLogging(settings);
+  CHECK(logging_res);
+}
+
+constexpr char kSwitchHelp[] = "h";
+constexpr char kSwitchAllocPerSec[] = "mb_alloc_per_sec";
+constexpr char kSwitchReadPerSec[] = "mb_read_per_sec";
+constexpr char kSwitchWritePerSec[] = "mb_write_per_sec";
+constexpr char kSwitchAllocLimit[] = "alloc_limit";
+constexpr char kSwitchStartTimeout[] = "start_timeout";
+constexpr char kSwitchReadTimeout[] = "read_timeout";
+constexpr char kSwitchWriteTimeout[] = "write_timeout";
+constexpr char kSwitchFreeTimeout[] = "free_timeout";
+constexpr char kSwitchExitTimeout[] = "exit_timeout";
+constexpr char kUsageString[] = R"(Usage: memory_simulator [options]
+
+A tool to allocate, read and write memory pages at a configurable frequency.
+
+Options:
+  --mb_alloc_per_sec     Megabytes allocated per second
+  --mb_read_per_sec      Megabytes read per second
+  --mb_write_per_sec     Megabytes written per second
+  --alloc_limit          Stop allocating after this limit (megabytes)
+  --start_timeout        Start alloc/read/write after this timeout (seconds)
+  --read_timeout         Stop reading after this timeout (seconds)
+  --write_timeout        Stop writing reaching this timeout (seconds)
+  --free_timeout         Free all memory after this timeout (seconds)
+  --exit_timeout        Exit after this timeout (seconds)
+
+All options can be set to inifinite with the value "inf".
+)";
+
+void PrintUsageError(const std::string error) {
+  std::cerr << "Error: " << error << std::endl << std::endl << kUsageString;
+}
+
+// Status code, which can also be used as process exit code. Therefore
+// success is explicitly 0.
+enum StatusCode {
+  kStatusSuccess = 0,
+  kStatusUsage = 1,
+  kStatusInvalidParam = 2,
+  kStatusRuntimeError = 3,
+};
+
+absl::optional<int64_t> GetInt64Switch(const base::CommandLine& command_line,
+                                       const std::string& switch_name,
+                                       int64_t default_value) {
+  if (!command_line.HasSwitch(switch_name)) {
+    return default_value;
+  }
+
+  const std::string switch_value =
+      command_line.GetSwitchValueASCII(switch_name);
+  if (switch_value == "inf") {
+    return std::numeric_limits<int64_t>::max();
+  }
+
+  int64_t switch_value_int64 = 0;
+  if (!base::StringToInt64(switch_value, &switch_value_int64) ||
+      switch_value_int64 < 0) {
+    PrintUsageError(base::StringPrintf("Switch %s must be a positive integer",
+                                       switch_name.c_str()));
+    return absl::nullopt;
+  }
+
+  return switch_value_int64;
+}
+
+absl::optional<base::TimeDelta> GetSecondsSwitch(
+    const base::CommandLine& command_line,
+    const std::string& switch_name,
+    int64_t default_value) {
+  absl::optional<int64_t> seconds =
+      GetInt64Switch(command_line, switch_name, default_value);
+  if (!seconds.has_value()) {
+    return absl::nullopt;
+  }
+
+  if (seconds.value() == std::numeric_limits<int64_t>::max()) {
+    return base::TimeDelta::Max();
+  }
+
+  return base::Seconds(seconds.value());
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  base::CommandLine::Init(argc, argv);
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+  InitLogging();
+
+#if DCHECK_IS_ON()
+  LOG(WARNING) << "!!!!!!";
+  LOG(WARNING) << "memory_simulator was built with DCHECKs enabled. This "
+                  "changes the behavior of alloc/free and may affect results.";
+  LOG(WARNING) << "!!!!!!";
+#endif
+
+  if (command_line.HasSwitch(kSwitchHelp)) {
+    std::cerr << kUsageString;
+    return kStatusUsage;
+  }
+
+  absl::optional<int64_t> mb_alloc_per_sec =
+      GetInt64Switch(command_line, kSwitchAllocPerSec, 512);
+  if (!mb_alloc_per_sec.has_value()) {
+    return kStatusInvalidParam;
+  }
+
+  absl::optional<int64_t> mb_read_per_sec =
+      GetInt64Switch(command_line, kSwitchReadPerSec, 1024);
+  if (!mb_read_per_sec.has_value()) {
+    return kStatusInvalidParam;
+  }
+
+  absl::optional<int64_t> mb_write_per_sec =
+      GetInt64Switch(command_line, kSwitchWritePerSec, 1024);
+  if (!mb_write_per_sec.has_value()) {
+    return kStatusInvalidParam;
+  }
+
+  absl::optional<int64_t> mb_alloc_limit =
+      GetInt64Switch(command_line, kSwitchAllocLimit, 10 * 1024);
+  if (!mb_alloc_limit.has_value()) {
+    return kStatusInvalidParam;
+  }
+
+  absl::optional<base::TimeDelta> start_timeout =
+      GetSecondsSwitch(command_line, kSwitchStartTimeout, 0);
+  if (!start_timeout.has_value()) {
+    return kStatusInvalidParam;
+  }
+
+  absl::optional<base::TimeDelta> read_timeout =
+      GetSecondsSwitch(command_line, kSwitchReadTimeout, 5 * 60);
+  if (!read_timeout.has_value()) {
+    return kStatusInvalidParam;
+  }
+
+  absl::optional<base::TimeDelta> write_timeout =
+      GetSecondsSwitch(command_line, kSwitchWriteTimeout, 5 * 60);
+  if (!write_timeout.has_value()) {
+    return kStatusInvalidParam;
+  }
+
+  absl::optional<base::TimeDelta> free_timeout =
+      GetSecondsSwitch(command_line, kSwitchFreeTimeout, 10 * 60);
+  if (!free_timeout.has_value()) {
+    return kStatusInvalidParam;
+  }
+
+  absl::optional<base::TimeDelta> exit_timeout =
+      GetSecondsSwitch(command_line, kSwitchExitTimeout, 5 * 60);
+  if (!exit_timeout.has_value()) {
+    return kStatusInvalidParam;
+  }
+
+  base::SingleThreadTaskExecutor executor;
+  base::RunLoop run_loop;
+
+  memory_simulator::MemorySimulator simulator;
+
+  base::OneShotTimer free_timer;
+  free_timer.Start(
+      FROM_HERE, free_timeout.value(),
+      base::BindOnce(&memory_simulator::MemorySimulator::StopAndFree,
+                     base::Unretained(&simulator)));
+
+  base::OneShotTimer exit_timer;
+  exit_timer.Start(FROM_HERE, exit_timeout.value(), run_loop.QuitClosure());
+
+  memory_simulator::MetricsPrinter printer;
+  printer.AddProvider(
+      std::make_unique<memory_simulator::SimulatorMetricsProvider>(&simulator));
+#if BUILDFLAG(IS_MAC)
+  printer.AddProvider(
+      std::make_unique<memory_simulator::ProcessMetricsProviderMac>());
+  printer.AddProvider(
+      std::make_unique<memory_simulator::SystemMetricsProviderMac>());
+#endif  // BUILDFLAG(IS_MAC)
+
+  base::RepeatingTimer stats_timer;
+  printer.PrintHeader();
+  stats_timer.Start(
+      FROM_HERE, base::Seconds(5),
+      base::BindRepeating(&memory_simulator::MetricsPrinter::PrintStats,
+                          base::Unretained(&printer)));
+
+  base::TimeTicks now = base::TimeTicks::Now();
+  base::TimeTicks read_deadline = now + read_timeout.value();
+  base::TimeTicks write_deadline = now + write_timeout.value();
+
+  base::OneShotTimer start_timer;
+  start_timer.Start(
+      FROM_HERE, start_timeout.value(),
+      base::BindOnce(&memory_simulator::MemorySimulator::Start,
+                     base::Unretained(&simulator),
+                     memory_simulator::MBToPages(mb_alloc_per_sec.value()),
+                     memory_simulator::MBToPages(mb_read_per_sec.value()),
+                     memory_simulator::MBToPages(mb_write_per_sec.value()),
+                     memory_simulator::MBToPages(mb_alloc_limit.value()),
+                     read_deadline, write_deadline));
+
+  run_loop.Run();
+
+  return kStatusSuccess;
+}
diff --git a/tools/memory/simulator/metrics_printer.cc b/tools/memory/simulator/metrics_printer.cc
new file mode 100644
index 0000000..c4d676f
--- /dev/null
+++ b/tools/memory/simulator/metrics_printer.cc
@@ -0,0 +1,64 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/memory/simulator/metrics_printer.h"
+
+#include <iostream>
+
+#include "base/strings/stringprintf.h"
+
+namespace memory_simulator {
+
+MetricsPrinter::MetricsPrinter() = default;
+
+MetricsPrinter::~MetricsPrinter() = default;
+
+void MetricsPrinter::AddProvider(std::unique_ptr<MetricsProvider> provider) {
+  DCHECK(!did_print_header_);
+  providers_.push_back(std::move(provider));
+}
+
+void MetricsPrinter::PrintHeader() {
+  DCHECK(!did_print_header_);
+  did_print_header_ = true;
+
+  std::string out = "elapsed_time(s)";
+
+  for (auto& provider : providers_) {
+    std::vector<std::string> metric_names = provider->GetMetricNames();
+    for (const std::string& metric_name : metric_names) {
+      base::StringAppendF(&out, ",%s", metric_name.c_str());
+    }
+  }
+
+  std::cout << out << std::endl;
+}
+
+void MetricsPrinter::PrintStats() {
+  DCHECK(did_print_header_);
+
+  std::string out;
+
+  base::TimeTicks now = base::TimeTicks::Now();
+  double seconds_since_start = (now - start_time_).InSecondsF();
+  base::StringAppendF(&out, "%.2f", seconds_since_start);
+
+  for (auto& provider : providers_) {
+    std::vector<std::string> metric_names = provider->GetMetricNames();
+    std::map<std::string, double> metric_values =
+        provider->GetMetricValues(now);
+    for (const std::string& metric_name : metric_names) {
+      auto it = metric_values.find(metric_name);
+      if (it == metric_values.end()) {
+        out += ",";
+      } else {
+        base::StringAppendF(&out, ",%.2f", it->second);
+      }
+    }
+  }
+
+  std::cout << out << std::endl;
+}
+
+}  // namespace memory_simulator
diff --git a/tools/memory/simulator/metrics_printer.h b/tools/memory/simulator/metrics_printer.h
new file mode 100644
index 0000000..ac8421c
--- /dev/null
+++ b/tools/memory/simulator/metrics_printer.h
@@ -0,0 +1,43 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_MEMORY_SIMULATOR_METRICS_PRINTER_H_
+#define TOOLS_MEMORY_SIMULATOR_METRICS_PRINTER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/time/time.h"
+#include "tools/memory/simulator/metrics_provider.h"
+
+namespace memory_simulator {
+
+// Outputs memory stats in CSV format to stdout.
+class MetricsPrinter {
+ public:
+  MetricsPrinter();
+  ~MetricsPrinter();
+
+  // Adds a metrics provider. This can only be called before the first call to
+  // PrintHeader().
+  void AddProvider(std::unique_ptr<MetricsProvider> provider);
+
+  // Prints header row.
+  void PrintHeader();
+
+  // Prints stats row. This can only be called after PrintHeader() has been
+  // called.
+  void PrintStats();
+
+ private:
+  const base::TimeTicks start_time_ = base::TimeTicks::Now();
+
+  bool did_print_header_ = false;
+
+  std::vector<std::unique_ptr<MetricsProvider>> providers_;
+};
+
+}  // namespace memory_simulator
+
+#endif  // TOOLS_MEMORY_SIMULATOR_METRICS_PRINTER_H_
diff --git a/tools/memory/simulator/metrics_provider.cc b/tools/memory/simulator/metrics_provider.cc
new file mode 100644
index 0000000..d137254
--- /dev/null
+++ b/tools/memory/simulator/metrics_provider.cc
@@ -0,0 +1,12 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/memory/simulator/metrics_provider.h"
+
+namespace memory_simulator {
+
+MetricsProvider::MetricsProvider() = default;
+MetricsProvider::~MetricsProvider() = default;
+
+}  // namespace memory_simulator
diff --git a/tools/memory/simulator/metrics_provider.h b/tools/memory/simulator/metrics_provider.h
new file mode 100644
index 0000000..b66bef5
--- /dev/null
+++ b/tools/memory/simulator/metrics_provider.h
@@ -0,0 +1,32 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_MEMORY_SIMULATOR_METRICS_PROVIDER_H_
+#define TOOLS_MEMORY_SIMULATOR_METRICS_PROVIDER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+
+namespace memory_simulator {
+
+class MetricsProvider {
+ public:
+  MetricsProvider();
+  virtual ~MetricsProvider();
+
+  // Returns the list of metrics that can be returned by this provider. This
+  // must always return the same metrics, in the same order.
+  virtual std::vector<std::string> GetMetricNames() = 0;
+
+  // Returns metric values. All keys must be part of `GetMetricNames()`.
+  virtual std::map<std::string, double> GetMetricValues(
+      base::TimeTicks now) = 0;
+};
+
+}  // namespace memory_simulator
+
+#endif  // TOOLS_MEMORY_SIMULATOR_METRICS_PROVIDER_H_
diff --git a/tools/memory/simulator/process_metrics_provider_mac.cc b/tools/memory/simulator/process_metrics_provider_mac.cc
new file mode 100644
index 0000000..c68f1d3
--- /dev/null
+++ b/tools/memory/simulator/process_metrics_provider_mac.cc
@@ -0,0 +1,47 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/memory/simulator/process_metrics_provider_mac.h"
+
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach/mach_vm.h>
+
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/time/time.h"
+#include "tools/memory/simulator/utils.h"
+
+namespace memory_simulator {
+
+ProcessMetricsProviderMac::ProcessMetricsProviderMac() = default;
+ProcessMetricsProviderMac::~ProcessMetricsProviderMac() = default;
+
+std::vector<std::string> ProcessMetricsProviderMac::GetMetricNames() {
+  return {"process_resident(gb)", "process_internal(gb)",
+          "process_external(gb)", "process_compressed(gb)",
+          "process_physical(gb)"};
+}
+
+std::map<std::string, double> ProcessMetricsProviderMac::GetMetricValues(
+    base::TimeTicks now) {
+  std::map<std::string, double> metrics;
+
+  task_vm_info process_info;
+  mach_msg_type_number_t count = TASK_VM_INFO_REV2_COUNT;
+  kern_return_t result =
+      task_info(mach_task_self(), TASK_VM_INFO,
+                reinterpret_cast<task_info_t>(&process_info), &count);
+  CHECK_EQ(result, KERN_SUCCESS);
+
+  metrics["process_resident(gb)"] = BytesToGB(process_info.resident_size);
+  metrics["process_internal(gb)"] = BytesToGB(process_info.internal);
+  metrics["process_external(gb)"] = BytesToGB(process_info.external);
+  metrics["process_compressed(gb)"] = BytesToGB(process_info.compressed);
+  metrics["process_physical(gb)"] = BytesToGB(process_info.phys_footprint);
+
+  return metrics;
+}
+
+}  // namespace memory_simulator
diff --git a/tools/memory/simulator/process_metrics_provider_mac.h b/tools/memory/simulator/process_metrics_provider_mac.h
new file mode 100644
index 0000000..2dabc13
--- /dev/null
+++ b/tools/memory/simulator/process_metrics_provider_mac.h
@@ -0,0 +1,24 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_MEMORY_SIMULATOR_PROCESS_METRICS_PROVIDER_MAC_H_
+#define TOOLS_MEMORY_SIMULATOR_PROCESS_METRICS_PROVIDER_MAC_H_
+
+#include "tools/memory/simulator/metrics_provider.h"
+
+namespace memory_simulator {
+
+class ProcessMetricsProviderMac : public MetricsProvider {
+ public:
+  ProcessMetricsProviderMac();
+  ~ProcessMetricsProviderMac() override;
+
+  // MetricsProvider:
+  std::vector<std::string> GetMetricNames() override;
+  std::map<std::string, double> GetMetricValues(base::TimeTicks now) override;
+};
+
+}  // namespace memory_simulator
+
+#endif  // TOOLS_MEMORY_SIMULATOR_PROCESS_METRICS_PROVIDER_MAC_H_
diff --git a/tools/memory/simulator/simulator_metrics_provider.cc b/tools/memory/simulator/simulator_metrics_provider.cc
new file mode 100644
index 0000000..4ba4ac6b
--- /dev/null
+++ b/tools/memory/simulator/simulator_metrics_provider.cc
@@ -0,0 +1,50 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/memory/simulator/simulator_metrics_provider.h"
+
+#include "base/time/time.h"
+#include "tools/memory/simulator/memory_simulator.h"
+#include "tools/memory/simulator/utils.h"
+
+namespace memory_simulator {
+
+SimulatorMetricsProvider::SimulatorMetricsProvider(MemorySimulator* simulator)
+    : simulator_(simulator) {}
+
+SimulatorMetricsProvider::~SimulatorMetricsProvider() = default;
+
+std::vector<std::string> SimulatorMetricsProvider::GetMetricNames() {
+  return {"simulator_allocated(gb)", "simulator_allocation_rate(mb/s)",
+          "simulator_read_rate(mb/s)", "simulator_write_rate(mb/s)"};
+}
+
+std::map<std::string, double> SimulatorMetricsProvider::GetMetricValues(
+    base::TimeTicks now) {
+  int64_t pages_allocated = simulator_->GetPagesAllocated();
+  int64_t pages_read = simulator_->GetPagesRead();
+  int64_t pages_written = simulator_->GetPagesWritten();
+
+  std::map<std::string, double> metrics;
+  metrics["simulator_allocated(gb)"] = PagesToGB(pages_allocated);
+
+  if (!prev_time_.is_null()) {
+    base::TimeDelta elapsed = now - prev_time_;
+    metrics["simulator_allocation_rate(mb/s)"] =
+        PagesToMBPerSec(prev_pages_allocated_, pages_allocated, elapsed);
+    metrics["simulator_read_rate(mb/s)"] =
+        PagesToMBPerSec(prev_pages_read_, pages_read, elapsed);
+    metrics["simulator_write_rate(mb/s)"] =
+        PagesToMBPerSec(prev_pages_written_, pages_written, elapsed);
+  }
+
+  prev_time_ = now;
+  prev_pages_allocated_ = pages_allocated;
+  prev_pages_read_ = pages_read;
+  prev_pages_written_ = pages_written;
+
+  return metrics;
+}
+
+}  // namespace memory_simulator
diff --git a/tools/memory/simulator/simulator_metrics_provider.h b/tools/memory/simulator/simulator_metrics_provider.h
new file mode 100644
index 0000000..7a94b673
--- /dev/null
+++ b/tools/memory/simulator/simulator_metrics_provider.h
@@ -0,0 +1,39 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_MEMORY_SIMULATOR_SIMULATOR_METRICS_PROVIDER_H_
+#define TOOLS_MEMORY_SIMULATOR_SIMULATOR_METRICS_PROVIDER_H_
+
+#include <stdint.h>
+#include <cstdint>
+
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "tools/memory/simulator/metrics_provider.h"
+
+namespace memory_simulator {
+
+class MemorySimulator;
+
+class SimulatorMetricsProvider : public MetricsProvider {
+ public:
+  explicit SimulatorMetricsProvider(MemorySimulator* simulator);
+  ~SimulatorMetricsProvider() override;
+
+  // MetricsProvider:
+  std::vector<std::string> GetMetricNames() override;
+  std::map<std::string, double> GetMetricValues(base::TimeTicks now) override;
+
+ private:
+  raw_ptr<MemorySimulator> simulator_;
+
+  base::TimeTicks prev_time_;
+  int64_t prev_pages_allocated_ = 0;
+  int64_t prev_pages_read_ = 0;
+  int64_t prev_pages_written_ = 0;
+};
+
+}  // namespace memory_simulator
+
+#endif  // TOOLS_MEMORY_SIMULATOR_SIMULATOR_METRICS_PROVIDER_H_
diff --git a/tools/memory/simulator/system_metrics_provider_mac.cc b/tools/memory/simulator/system_metrics_provider_mac.cc
new file mode 100644
index 0000000..e557cb8
--- /dev/null
+++ b/tools/memory/simulator/system_metrics_provider_mac.cc
@@ -0,0 +1,117 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/memory/simulator/system_metrics_provider_mac.h"
+
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/time/time.h"
+#include "tools/memory/simulator/utils.h"
+
+namespace memory_simulator {
+
+SystemMetricsProviderMac::SystemMetricsProviderMac() = default;
+SystemMetricsProviderMac::~SystemMetricsProviderMac() = default;
+
+std::vector<std::string> SystemMetricsProviderMac::GetMetricNames() {
+  return {"total(gb)",
+          "free(gb)",
+          "active(gb)",
+          "inactive(gb)",
+          "wired(gb)",
+          "purgeable_count(gb)",
+          "speculative(gb)",
+          "compressor(gb)",
+          "throttled(gb)",
+          "file_backed(gb)",
+          "anonymous(gb)",
+          "uncompressed_in_compressor(gb)",
+          "reactivations(mb/s)",
+          "pageins(mb/s)",
+          "pageouts(mb/s)",
+          "faults(mb/s)",
+          "cow_faults(mb/s)",
+          "cache_lookups(mb/s)",
+          "cache_hits(mb/s)",
+          "purges(mb/s)",
+          "decompressions(mb/s)",
+          "compressions(mb/s)",
+          "swapins(mb/s)",
+          "swapouts(mb/s)"};
+}
+
+std::map<std::string, double> SystemMetricsProviderMac::GetMetricValues(
+    base::TimeTicks now) {
+  std::map<std::string, double> metrics;
+
+  struct host_basic_info hostinfo;
+  mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+  base::mac::ScopedMachSendRight host(mach_host_self());
+  int result = host_info(host.get(), HOST_BASIC_INFO,
+                         reinterpret_cast<host_info_t>(&hostinfo), &count);
+  CHECK_EQ(result, KERN_SUCCESS);
+
+  CHECK_EQ(HOST_BASIC_INFO_COUNT, count);
+  metrics["total(gb)"] = BytesToGB(hostinfo.max_mem);
+
+  vm_statistics64_data_t vm_info;
+  count = HOST_VM_INFO64_COUNT;
+  CHECK_EQ(host_statistics64(host.get(), HOST_VM_INFO64,
+                             reinterpret_cast<host_info64_t>(&vm_info), &count),
+           KERN_SUCCESS);
+  CHECK_EQ(HOST_VM_INFO64_COUNT, count);
+
+  // Free memory includes speculative memory.
+  metrics["free(gb)"] = PagesToGB(vm_info.free_count);
+  metrics["active(gb)"] = PagesToGB(vm_info.active_count);
+  metrics["inactive(gb)"] = PagesToGB(vm_info.inactive_count);
+  metrics["wired(gb)"] = PagesToGB(vm_info.wire_count);
+  metrics["purgeable_count(gb)"] = PagesToGB(vm_info.purgeable_count);
+  metrics["speculative(gb)"] = PagesToGB(vm_info.speculative_count);
+  metrics["compressor(gb)"] = PagesToGB(vm_info.compressor_page_count);
+  metrics["throttled(gb)"] = PagesToGB(vm_info.throttled_count);
+  metrics["file_backed(gb)"] = PagesToGB(vm_info.external_page_count);
+  metrics["anonymous(gb)"] = PagesToGB(vm_info.internal_page_count);
+  metrics["uncompressed_in_compressor(gb)"] =
+      PagesToGB(vm_info.total_uncompressed_pages_in_compressor);
+
+  if (!prev_time_.is_null()) {
+    const base::TimeDelta elapsed = now - prev_time_;
+
+    metrics["reactivations(mb/s)"] = PagesToMBPerSec(
+        prev_vm_info_.reactivations, vm_info.reactivations, elapsed);
+    metrics["pageins(mb/s)"] =
+        PagesToMBPerSec(prev_vm_info_.pageins, vm_info.pageins, elapsed);
+    metrics["pageouts(mb/s)"] =
+        PagesToMBPerSec(prev_vm_info_.pageouts, vm_info.pageouts, elapsed);
+    metrics["faults(mb/s)"] =
+        PagesToMBPerSec(prev_vm_info_.faults, vm_info.faults, elapsed);
+    metrics["cow_faults(mb/s)"] =
+        PagesToMBPerSec(prev_vm_info_.cow_faults, vm_info.cow_faults, elapsed);
+    metrics["cache_lookups(mb/s)"] =
+        PagesToMBPerSec(prev_vm_info_.lookups, vm_info.lookups, elapsed);
+    metrics["cache_hits(mb/s)"] =
+        PagesToMBPerSec(prev_vm_info_.hits, vm_info.hits, elapsed);
+    metrics["purges(mb/s)"] =
+        PagesToMBPerSec(prev_vm_info_.purges, vm_info.purges, elapsed);
+    metrics["decompressions(mb/s)"] = PagesToMBPerSec(
+        prev_vm_info_.decompressions, vm_info.decompressions, elapsed);
+    metrics["compressions(mb/s)"] = PagesToMBPerSec(
+        prev_vm_info_.compressions, vm_info.compressions, elapsed);
+    metrics["swapins(mb/s)"] =
+        PagesToMBPerSec(prev_vm_info_.swapins, vm_info.swapins, elapsed);
+    metrics["swapouts(mb/s)"] =
+        PagesToMBPerSec(prev_vm_info_.swapouts, vm_info.swapouts, elapsed);
+  }
+
+  prev_time_ = now;
+  prev_vm_info_ = vm_info;
+
+  return metrics;
+}
+
+}  // namespace memory_simulator
diff --git a/tools/memory/simulator/system_metrics_provider_mac.h b/tools/memory/simulator/system_metrics_provider_mac.h
new file mode 100644
index 0000000..4c6a41d
--- /dev/null
+++ b/tools/memory/simulator/system_metrics_provider_mac.h
@@ -0,0 +1,30 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_MEMORY_SIMULATOR_SYSTEM_METRICS_PROVIDER_MAC_H_
+#define TOOLS_MEMORY_SIMULATOR_SYSTEM_METRICS_PROVIDER_MAC_H_
+
+#include <mach/mach_vm.h>
+
+#include "tools/memory/simulator/metrics_provider.h"
+
+namespace memory_simulator {
+
+class SystemMetricsProviderMac : public MetricsProvider {
+ public:
+  SystemMetricsProviderMac();
+  ~SystemMetricsProviderMac() override;
+
+  // MetricsProvider:
+  std::vector<std::string> GetMetricNames() override;
+  std::map<std::string, double> GetMetricValues(base::TimeTicks now) override;
+
+ private:
+  base::TimeTicks prev_time_;
+  vm_statistics64_data_t prev_vm_info_;
+};
+
+}  // namespace memory_simulator
+
+#endif  // TOOLS_MEMORY_SIMULATOR_SYSTEM_METRICS_PROVIDER_MAC_H_
diff --git a/tools/memory/simulator/utils.cc b/tools/memory/simulator/utils.cc
new file mode 100644
index 0000000..73f20555
--- /dev/null
+++ b/tools/memory/simulator/utils.cc
@@ -0,0 +1,37 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/memory/simulator/utils.h"
+
+#include "base/memory/page_size.h"
+
+namespace memory_simulator {
+
+double BytesToGB(uint64_t bytes) {
+  return static_cast<double>(bytes) / (1024 * 1024 * 1024);
+}
+
+double BytesToMB(uint64_t bytes) {
+  return static_cast<double>(bytes) / (1024 * 1024);
+}
+
+double PagesToGB(uint64_t pages) {
+  return BytesToGB(pages * base::GetPageSize());
+}
+
+double PagesToMB(uint64_t pages) {
+  return BytesToMB(pages * base::GetPageSize());
+}
+
+int64_t MBToPages(uint64_t mb) {
+  return mb * 1024 * 1024 / base::GetPageSize();
+}
+
+double PagesToMBPerSec(int64_t pages_begin,
+                       int64_t pages_end,
+                       base::TimeDelta duration) {
+  return PagesToMB(pages_end - pages_begin) / duration.InSecondsF();
+}
+
+}  // namespace memory_simulator
diff --git a/tools/memory/simulator/utils.h b/tools/memory/simulator/utils.h
new file mode 100644
index 0000000..7a1c297
--- /dev/null
+++ b/tools/memory/simulator/utils.h
@@ -0,0 +1,29 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_MEMORY_SIMULATOR_UTILS_H_
+#define TOOLS_MEMORY_SIMULATOR_UTILS_H_
+
+#include <stdint.h>
+
+#include "base/time/time.h"
+
+namespace memory_simulator {
+
+// Convert between bytes, GB, MB and pages.
+double BytesToGB(uint64_t bytes);
+double BytesToMB(uint64_t bytes);
+double PagesToGB(uint64_t pages);
+double PagesToMB(uint64_t pages);
+int64_t MBToPages(uint64_t mb);
+
+// Computes the amount of Megabytes per Second from a number of page at the
+// beginning and end of an interval and the duration of the interval.
+double PagesToMBPerSec(int64_t pages_begin,
+                       int64_t pages_end,
+                       base::TimeDelta duration);
+
+}  // namespace memory_simulator
+
+#endif  // TOOLS_MEMORY_SIMULATOR_UTILS_H_
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index c099df1b..23f689a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -13863,6 +13863,8 @@
   <int value="1" label="get and observe via document api"/>
   <int value="2" label="get via fetch-like api"/>
   <int value="3" label="observe via fetch-like api"/>
+  <int value="4" label="get via iframe attribute api"/>
+  <int value="5" label="observe via iframe attribute api"/>
 </enum>
 
 <enum name="BrowsingTopicsCalculatorResultStatus">
@@ -35649,6 +35651,7 @@
   <int value="1774" label="PASSWORDSPRIVATE_CONTINUEIMPORT"/>
   <int value="1775" label="PASSWORDSPRIVATE_RESETIMPORTER"/>
   <int value="1776" label="SMARTCARDPROVIDERPRIVATE_REPORTCANCELRESULT"/>
+  <int value="1777" label="RUNTIME_GETCONTEXTS"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -58126,6 +58129,8 @@
   <int value="-1940806558" label="enable-syncfs-directory-operation"/>
   <int value="-1940377152" label="MacRTL:enabled"/>
   <int value="-1940291343" label="SpeculativeResourcePrefetching:enabled"/>
+  <int value="-1940251540"
+      label="AutofillEnableEmailOtpForVcnYellowPath:enabled"/>
   <int value="-1939525844" label="AssistantTimersV2:enabled"/>
   <int value="-1939016096"
       label="OmniboxUIExperimentHideSuggestionUrlTrivialSubdomains:enabled"/>
@@ -61188,6 +61193,7 @@
   <int value="-271084069" label="ClickToCallUI:enabled"/>
   <int value="-270626757" label="log-net-log"/>
   <int value="-270261701" label="WebRtcEnableCaptureMultiChannelApm:disabled"/>
+  <int value="-270124279" label="GetTheMostOutOfChrome:enabled"/>
   <int value="-269966631" label="UnifiedPasswordManagerErrorMessages:enabled"/>
   <int value="-269440655"
       label="AutofillDownstreamCvcPromptUseGooglePayLogo:disabled"/>
@@ -61321,6 +61327,8 @@
   <int value="-185162926" label="IncreaseInputAudioBufferSize:enabled"/>
   <int value="-184091779"
       label="OmniboxUIExperimentWhiteBackgroundOnBlur:enabled"/>
+  <int value="-183541827"
+      label="AutofillEnableEmailOtpForVcnYellowPath:disabled"/>
   <int value="-183246373" label="enable-multilingual-spellchecker"/>
   <int value="-181590721"
       label="AutofillEnforceMinRequiredFieldsForHeuristics:enabled"/>
@@ -64606,6 +64614,7 @@
   <int value="1637615389" label="PrintServerScaling:enabled"/>
   <int value="1638123293" label="DeprecateAltClick:disabled"/>
   <int value="1638582559" label="UpdatedCellularActivationUi:disabled"/>
+  <int value="1638617234" label="GetTheMostOutOfChrome:disabled"/>
   <int value="1638927687" label="AutofillRichMetadataQueries:disabled"/>
   <int value="1639190590" label="UseMessagesGoogleComDomain:enabled"/>
   <int value="1639314588" label="LookalikeUrlNavigationSuggestions:disabled"/>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 79d7e614..02c27e2 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -3585,7 +3585,7 @@
 
 <histogram
     name="Ash.NavigationWidget.AnimationSmoothness{HotseatTransitionType}"
-    units="%" expires_after="2023-04-20">
+    units="%" expires_after="2024-03-27">
   <owner>anasalazar@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
@@ -3601,7 +3601,7 @@
 
 <histogram
     name="Ash.NavigationWidget.{NavigationWidgetElement}AnimationSmoothness"
-    units="%" expires_after="2023-04-20">
+    units="%" expires_after="2024-03-27">
   <owner>anasalazar@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index 5e8ddfee..dbe7ce6 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -66,7 +66,7 @@
     <owner>chromeos-tango@google.com</owner>
   </variant>
   <variant name="FastPair" summary="Fast Pair">
-    <owner>shanefitz@google.com</owner>
+    <owner>jackshira@google.com</owner>
     <owner>julietlevesque@google.com</owner>
     <owner>chromeos-cross-device-eng@google.com</owner>
   </variant>
@@ -1139,7 +1139,7 @@
 </histogram>
 
 <histogram name="ChromeOS.FeatureUsage.{FeatureName}" enum="FeatureUsageEvent"
-    expires_after="2023-05-11">
+    expires_after="2024-04-01">
   <owner>rsorokin@chromium.org</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -1151,7 +1151,7 @@
 </histogram>
 
 <histogram name="ChromeOS.FeatureUsage.{FeatureName}.Usetime" units="ms"
-    expires_after="2023-05-11">
+    expires_after="2024-04-01">
   <owner>rsorokin@chromium.org</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cras/histograms.xml b/tools/metrics/histograms/metadata/cras/histograms.xml
index 0d450a0..aeb15df 100644
--- a/tools/metrics/histograms/metadata/cras/histograms.xml
+++ b/tools/metrics/histograms/metadata/cras/histograms.xml
@@ -390,6 +390,14 @@
   <summary>Records the source of a change to the input gain.</summary>
 </histogram>
 
+<histogram name="Cras.InputGainMutedSource" enum="AudioSettingsChangeSource"
+    expires_after="2024-02-14">
+  <owner>gavinwill@chromium.org</owner>
+  <owner>yuhsuan@chromium.org</owner>
+  <owner>chromeos-audio@google.com</owner>
+  <summary>Records the source of muting/unmuting the input gain.</summary>
+</histogram>
+
 <histogram name="Cras.kHfpWidebandSpeechSelectedCodec" enum="CrasHfpCodec"
     expires_after="2022-12-01">
   <owner>hychao@chromium.org</owner>
@@ -503,6 +511,14 @@
   <summary>Records the source of a change to the output volume.</summary>
 </histogram>
 
+<histogram name="Cras.OutputVolumeMutedSource" enum="AudioSettingsChangeSource"
+    expires_after="2024-02-14">
+  <owner>gavinwill@chromium.org</owner>
+  <owner>yuhsuan@chromium.org</owner>
+  <owner>chromeos-audio@google.com</owner>
+  <summary>Records the source of muting/unmuting the output volume.</summary>
+</histogram>
+
 <histogram name="Cras.RtcDevicePair" enum="CrasDevicePair"
     expires_after="2023-08-08">
   <owner>yuhsuan@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/input/histograms.xml b/tools/metrics/histograms/metadata/input/histograms.xml
index e22b7441..0a25f3fa 100644
--- a/tools/metrics/histograms/metadata/input/histograms.xml
+++ b/tools/metrics/histograms/metadata/input/histograms.xml
@@ -1724,6 +1724,9 @@
 
 <histogram name="Viewport.DidScalePage" enum="BooleanDidScalePage"
     expires_after="M108">
+  <obsolete>
+    Removed in M108.
+  </obsolete>
   <owner>bokan@chromium.org</owner>
   <owner>input-dev@chromium.org</owner>
   <summary>
@@ -1736,6 +1739,9 @@
 
 <histogram name="Viewport.MaxPageScale" enum="PageScaleFactorRange"
     expires_after="M108">
+  <obsolete>
+    Removed in M108.
+  </obsolete>
   <owner>bokan@chromium.org</owner>
   <owner>input-dev@chromium.org</owner>
   <summary>
@@ -1749,7 +1755,7 @@
 </histogram>
 
 <histogram name="Viewport.MetaTagType" enum="MetaTagTypeEnum"
-    expires_after="M108">
+    expires_after="2024-03-23">
   <owner>bokan@chromium.org</owner>
   <owner>input-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index bb8b5a4c..aeb8adc 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -524,6 +524,31 @@
   </summary>
 </histogram>
 
+<histogram name="Memory.Experimental.AvailableMemoryMB" units="MB"
+    expires_after="2023-09-22">
+  <owner>anthonyvd@chromium.org</owner>
+  <owner>catan-team@chromium.org</owner>
+  <summary>
+    The amount of available physical memory on the system as reported by
+    base::SysInfo::AmountOfAvailablePhysicalMemory().
+
+    Recorded every 2 minutes.
+  </summary>
+</histogram>
+
+<histogram name="Memory.Experimental.AvailableMemoryPercent" units="%"
+    expires_after="2023-09-22">
+  <owner>anthonyvd@chromium.org</owner>
+  <owner>catan-team@chromium.org</owner>
+  <summary>
+    The percentage of physical memory on the system considered
+    &quot;available&quot; by base::SysInfo::AmountOfAvailablePhysicalMemory()
+    and base::SysInfo::AmountOfPhysicalMemory().
+
+    Recorded every 2 minutes.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Memory.Experimental.Browser2" units="MB"
     expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocator2" -->
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index 8c82c59..f919a07 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -2205,29 +2205,6 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.UserClassifier.AverageHoursToOpenNTP" units="hours"
-    expires_after="2021-09-05">
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The estimated average number of hours between two successive times
-    when a new tab page is opened. Recorded after each opening of a NTP (and
-    after updating the model used for the estimate).
-  </summary>
-</histogram>
-
-<histogram name="NewTabPage.UserClassifier.AverageHoursToShowSuggestions"
-    units="hours" expires_after="2021-09-05">
-  <owner>freedjm@chromium.org</owner>
-  <owner>feed@chromium.org</owner>
-  <summary>
-    Android: The estimated average number of hours between two successive times
-    when the list of content suggestions on a new tab page is shown (i.e. when
-    the user scrolls below the fold). Recorded after each time the suggestions
-    are shown (and after updating the model used for the estimate).
-  </summary>
-</histogram>
-
 <histogram name="NewTabPage.UserClassifier.AverageHoursToUseSuggestions"
     units="hours" expires_after="2022-01-09">
   <owner>freedjm@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index c0736a79..7bee9a25 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -612,7 +612,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.OptimizationHintsComponent.MajorVersion"
-    units="major version number" expires_after="2023-05-07">
+    units="major version number" expires_after="M117">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml
index b36b514..6a0c172 100644
--- a/tools/metrics/histograms/metadata/power/histograms.xml
+++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -633,7 +633,7 @@
 </histogram>
 
 <histogram name="Power.AdaptiveChargingMinutes.{ReportType}" units="minutes"
-    expires_after="2023-03-19">
+    expires_after="2024-03-23">
   <owner>dbasehore@chromium.org</owner>
   <owner>chromeos-platform-power@google.com</owner>
   <summary>
@@ -2294,7 +2294,7 @@
 </histogram>
 
 <histogram name="Power.ShutdownReason" enum="ShutdownReason"
-    expires_after="M85">
+    expires_after="2024-03-23">
   <owner>tbroch@chromium.org</owner>
   <summary>
     The reason for the Chrome OS power manager shutting down or rebooting the
@@ -2369,7 +2369,7 @@
 </histogram>
 
 <histogram name="Power.TimeInSuspendAtBoot" units="minutes"
-    expires_after="2021-03-15">
+    expires_after="2024-03-23">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS time in minutes spent in suspend-to-RAM mode sampled at boot
@@ -2378,7 +2378,7 @@
 </histogram>
 
 <histogram name="Power.TimeInSuspendAtResume" units="minutes"
-    expires_after="2021-04-04">
+    expires_after="2024-03-23">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS time in minutes spent in suspend-to-RAM mode sampled at resume.
@@ -2386,7 +2386,7 @@
 </histogram>
 
 <histogram name="Power.UserBrightnessAdjustmentsPerSessionOnAC" units="units"
-    expires_after="2022-05-01">
+    expires_after="2024-03-23">
   <owner>tbroch@chromium.org</owner>
   <owner>napper@chromium.org</owner>
   <summary>
@@ -2397,7 +2397,7 @@
 </histogram>
 
 <histogram name="Power.UserBrightnessAdjustmentsPerSessionOnBattery"
-    units="units" expires_after="2021-10-25">
+    units="units" expires_after="2024-03-23">
   <owner>tbroch@chromium.org</owner>
   <owner>napper@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 44a7ab3..e9950d8 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -60,31 +60,6 @@
   <variant name="URT" summary="the URL real-time mechanism"/>
 </variants>
 
-<variants name="MetricsCollectorBypassEventType">
-  <variant name="CsdInterstitialBypass"
-      summary="client-side detection interstitial bypass"/>
-  <variant name="DangerousDownloadBypass"
-      summary="dangerous download warning bypass"/>
-  <variant name="DatabaseInterstitialBypass"
-      summary="Safe Browsing database interstitial bypass"/>
-  <variant name="ExtensionAllowlistInstallBypass"
-      summary="extension allowlist install bypass"/>
-  <variant name="NonAllowlistedExtensionReEnabled"
-      summary="non allowlisted extension re-enabled"/>
-  <variant name="PasswordReuseModalBypass"
-      summary="password reuse modal warning bypass"/>
-  <variant name="RealTimeInterstitialBypass"
-      summary="real time URL check warning bypass"/>
-</variants>
-
-<variants name="MetricsCollectorSecuritySensitiveEventType">
-  <variant name="Download" summary="download"/>
-  <variant name="PasswordProtection" summary="password protection"/>
-  <variant name="SafeBrowsingInterstitial"
-      summary="safe browsing interstitial"/>
-  <variant name="SSLInterstitial" summary="SSL interstitial"/>
-</variants>
-
 <variants name="MetricsCollectorTimesDisabledEnabledDuration">
   <variant name="LongEnabled"
       summary="ESB enabled duration was greater than 24 hours"/>
@@ -494,35 +469,29 @@
 </histogram>
 
 <histogram
-    name="SafeBrowsing.Daily.BypassCountLast28Days.{UserState}.{EventType}"
-    units="events" expires_after="2024-01-04">
+    name="SafeBrowsing.Daily.BypassCountLast28Days.{UserState}.AllEvents"
+    units="events" expires_after="2024-03-23">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
-    Logs the total number of {EventType} in the last 28 days. Recorded for all
+    Logs the total number of bypass events in the last 28 days. Recorded for all
     non-Incognito profiles that are under {UserState} every 24 hours and at
     startup if the last recording was more than 24 hours ago.
   </summary>
   <token key="UserState" variants="MetricsCollectorUserState"/>
-  <token key="EventType" variants="MetricsCollectorBypassEventType">
-    <variant name="AllEvents" summary="any type of events"/>
-  </token>
 </histogram>
 
 <histogram
-    name="SafeBrowsing.Daily.SecuritySensitiveCountLast28Days.{UserState}.{EventType}"
-    units="events" expires_after="2023-03-08">
+    name="SafeBrowsing.Daily.SecuritySensitiveCountLast28Days.{UserState}.AllEvents"
+    units="events" expires_after="2024-03-23">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
-    Logs the total number of security sensitive {EventType} in the last 28 days.
+    Logs the total number of security sensitive events in the last 28 days.
     Recorded for all non-Incognito profiles that are under {UserState} every 24
     hours and at startup if the last recording was more than 24 hours ago.
   </summary>
   <token key="UserState" variants="MetricsCollectorUserState"/>
-  <token key="EventType" variants="MetricsCollectorSecuritySensitiveEventType">
-    <variant name="AllEvents" summary="any type of events"/>
-  </token>
 </histogram>
 
 <histogram name="SafeBrowsing.DelayedWarnings.Event"
@@ -568,35 +537,8 @@
   </summary>
 </histogram>
 
-<histogram name="SafeBrowsing.EsbDisabled.BypassCountLast28Days.{EventType}"
-    units="events" expires_after="2024-01-04">
-  <owner>xinghuilu@chromium.org</owner>
-  <owner>chrome-counter-abuse-alerts@google.com</owner>
-  <summary>
-    Records the total number of {EventType} in the last 28 days. Logged each
-    time a user disables Enhanced Safe Browsing. Logged at most three times a
-    week. If ESB is disabled for more than three times a week, later records
-    will be dropped.
-  </summary>
-  <token key="EventType" variants="MetricsCollectorBypassEventType"/>
-</histogram>
-
-<histogram name="SafeBrowsing.EsbDisabled.LastBypassEventInterval.{EventType}"
-    units="ms" expires_after="2024-01-04">
-  <owner>xinghuilu@chromium.org</owner>
-  <owner>chrome-counter-abuse-alerts@google.com</owner>
-  <summary>
-    Records the time between the last bypass event and when the user disables
-    Enhanced Safe Browsing. Logged each time a user disables Enhanced Safe
-    Browsing and the {EventType} is the last bypass event type. Logged at most
-    three times a week. If ESB is disabled for more than three times a week,
-    later records will be dropped.
-  </summary>
-  <token key="EventType" variants="MetricsCollectorBypassEventType"/>
-</histogram>
-
 <histogram name="SafeBrowsing.EsbDisabled.LastBypassEventType"
-    enum="SBMetricsCollectorEventType" expires_after="2023-08-27">
+    enum="SBMetricsCollectorEventType" expires_after="2024-03-23">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -618,23 +560,8 @@
   </summary>
 </histogram>
 
-<histogram
-    name="SafeBrowsing.EsbDisabled.LastSecuritySensitiveEventInterval.{EventType}"
-    units="ms" expires_after="2023-03-08">
-  <owner>xinghuilu@chromium.org</owner>
-  <owner>chrome-counter-abuse-alerts@google.com</owner>
-  <summary>
-    Records the time between the last security sensitive event and when the user
-    disables Enhanced Safe Browsing. Logged each time a user disables Enhanced
-    Safe Browsing and the {EventType} is the last security sensitive event type.
-    Logged at most three times a week. If ESB is disabled for more than three
-    times a week, later records will be dropped.
-  </summary>
-  <token key="EventType" variants="MetricsCollectorSecuritySensitiveEventType"/>
-</histogram>
-
 <histogram name="SafeBrowsing.EsbDisabled.LastSecuritySensitiveEventType"
-    enum="SBMetricsCollectorEventType" expires_after="2023-03-08">
+    enum="SBMetricsCollectorEventType" expires_after="2024-03-23">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -645,20 +572,6 @@
 </histogram>
 
 <histogram
-    name="SafeBrowsing.EsbDisabled.SecuritySensitiveCountLast28Days.{EventType}"
-    units="events" expires_after="2023-03-08">
-  <owner>xinghuilu@chromium.org</owner>
-  <owner>chrome-counter-abuse-alerts@google.com</owner>
-  <summary>
-    Records the total number of {EventType} in the last 28 days. Logged each
-    time a user disables Enhanced Safe Browsing. Logged at most three times a
-    week. If ESB is disabled for more than three times a week, later records
-    will be dropped.
-  </summary>
-  <token key="EventType" variants="MetricsCollectorSecuritySensitiveEventType"/>
-</histogram>
-
-<histogram
     name="SafeBrowsing.EsbDisabled.TimesDisabledLast28Days.{EnabledDuration}"
     units="times" expires_after="2023-08-28">
   <owner>thefrog@chromium.org</owner>
@@ -1411,16 +1324,6 @@
   </summary>
 </histogram>
 
-<histogram name="SafeBrowsing.MetricsCollector.IsPrefValid" enum="BooleanValid"
-    expires_after="2023-08-20">
-  <owner>xinghuilu@chromium.org</owner>
-  <owner>chrome-counter-abuse-alerts@google.com</owner>
-  <summary>
-    Records whether the pref for storing Safe Browsing events is valid. This is
-    logged each time old events are removed. It is logged daily.
-  </summary>
-</histogram>
-
 <histogram
     name="SafeBrowsing.NavigationObserver.MissingInitiatorRenderFrameHostPortal"
     enum="BooleanExists" expires_after="2023-04-23">
diff --git a/tools/rust/build_rust.py b/tools/rust/build_rust.py
index da5f66b..28df06e 100755
--- a/tools/rust/build_rust.py
+++ b/tools/rust/build_rust.py
@@ -63,18 +63,25 @@
                          VERSION_STAMP_PATH, GetPackageVersionForBuild)
 
 EXCLUDED_TESTS = [
-    # Temporarily disabled due to https://github.com/rust-lang/rust/issues/94322
-    os.path.join('tests', 'ui', 'numeric', 'numeric-cast.rs'),
-    # Temporarily disabled due to https://github.com/rust-lang/rust/issues/96497
+    # https://github.com/rust-lang/rust/issues/45222 which appears to have
+    # regressed as of a recent LLVM update. This test is purely performance
+    # related, not correctness.
+    os.path.join('tests', 'codegen', 'issue-45222.rs'),
+    # https://github.com/rust-lang/rust/issues/96497
     os.path.join('tests', 'codegen', 'issue-96497-slice-size-nowrap.rs'),
     # TODO(crbug.com/1347563): Re-enable when fixed.
     os.path.join('tests', 'codegen', 'sanitizer-cfi-emit-type-checks.rs'),
     os.path.join('tests', 'codegen',
                  'sanitizer-cfi-emit-type-metadata-itanium-cxx-abi.rs'),
-    # Temporarily disabled due to https://github.com/rust-lang/rust/issues/45222
-    # which appears to have regressed as of a recent LLVM update. This test is
-    # purely performance related, not correctness.
-    os.path.join('tests', 'codegen', 'issue-45222.rs')
+    # https://github.com/rust-lang/rust/issues/109671 the test is being
+    # optimized in newer LLVM which breaks its expectations.
+    os.path.join('tests', 'ui', 'abi', 'stack-protector.rs'),
+    # https://github.com/rust-lang/rust/issues/109672 the second panic in a
+    # double-panic is being optimized out (reasonably correctly) by newer LLVM.
+    os.path.join('tests', 'ui', 'backtrace.rs'),
+    # https://github.com/rust-lang/rust/issues/94322 large output from
+    # compiletests is breaking json parsing of the results.
+    os.path.join('tests', 'ui', 'numeric', 'numeric-cast.rs'),
 ]
 EXCLUDED_TESTS_WINDOWS = [
     # https://github.com/rust-lang/rust/issues/96464
diff --git a/tools/visual_debugger/connection.js b/tools/visual_debugger/connection.js
index c374c50..26e90980 100644
--- a/tools/visual_debugger/connection.js
+++ b/tools/visual_debugger/connection.js
@@ -8,52 +8,51 @@
 
 const Connection = {
 
-  getUrl() {
-    var request_error = "";
-    var server_error = "";
-    var url = "";
-    url = document.querySelector('#url').value;
-    if (!url) {
-      var http_requester = new XMLHttpRequest();
-      // Sync request to avoid complexity but is poor form.
-      try {
-        http_requester.open("GET", location.origin + '/discover.html', false);
-        http_requester.send();
-      }
-      catch (req_error) {
-        request_error = "Visual Debugger local server is inaccessible. \n" +
-          "Please launch the server with command:\n " +
-          "    ./launchdebugger {app_port} {remote_port} \n" +
-          " remote_port defaults to 7777 \n" +
-          " corresponds to the chromium command line\n    " +
-          " --remote-debugging-port=7777 \n" +
-          " app_port defaults to 8777. Currently app_port=" + location.port;
-      }
-
-      if (http_requester.status != 200) {
-        server_error = "Server reports error=" + http_requester.responseText;
-      }
-      else {
-        var discover_json = JSON.parse(http_requester.responseText);
-        url = discover_json.webSocketDebuggerUrl;
-      }
+  async getUrl() {
+    let url = document.querySelector("#url").value;
+    if (url) {
+      return [url, ""];
     }
-    const return_strings = [url, request_error, server_error];
-    return return_strings;
+
+    try {
+      let response = await fetch(location.origin + "/discover.json");
+      if (!response.ok) {
+        return [
+          "",
+          `Unexpected server error=${response.status} ${response.statusText}`,
+        ];
+      } else {
+        let discover_json = await response.json();
+        if (discover_json.error) {
+          // Error message from the python server
+          return ["", discover_json.error];
+        }
+
+        // Success
+        return [discover_json.webSocketDebuggerUrl, ""];
+      }
+    } catch (e) {
+      request_error =
+        "Visual Debugger local server is inaccessible. \n" +
+        "Please launch the server with command:\n " +
+        "    ./launchdebugger {app_port} {remote_port} \n" +
+        " remote_port defaults to 7777 \n" +
+        " corresponds to the chromium command line\n    " +
+        " --remote-debugging-port=7777 \n" +
+        " app_port defaults to 8777. Currently app_port=" +
+        location.port;
+      return ["", request_error];
+    }
   },
 
 
-  startConnection() {
+  async startConnection() {
     const loop_interval = 3000;
-    const connect_info = this.getUrl();
+    const connect_info = await this.getUrl();
     if (connect_info[1] != "") {
       window.alert(connect_info[1]);
       return;
     }
-    if (connect_info[2] != "") {
-      window.alert(connect_info[2]);
-      return;
-    }
     url = connect_info[0];
 
     // Create WebSocket connection.
@@ -111,18 +110,28 @@
       status.classList.add('disconnected');
       // Checks if connection can be made every
       // loop_interval number of milliseconds.
-      var testing = function() {
-        var interval = setInterval(function() {
-          if (document.getElementById('autoconnect').checked) {
-            const test_connect = Connection.getUrl();
-            if (test_connect[0] != "") {
-              clearInterval(interval);
-              Connection.startConnection();
-            }
+      let retryAfterDelay = () => {
+        setTimeout(() => {
+          if (!document.getElementById("autoconnect").checked) {
+            // Keep this setTimeout loop alive in case the user re-checks the
+            // box.
+            retryAfterDelay();
+            return;
           }
+
+          console.log("Attempting autoconnect...");
+          Connection.getUrl().then((test_connect) => {
+            if (test_connect[0] != "") {
+              Connection.startConnection();
+            } else {
+              // Failure, queue a retry.
+              retryAfterDelay();
+            }
+          });
         }, loop_interval);
-      }
-      testing();
+      };
+
+      retryAfterDelay();
     });
 
     disconnect.addEventListener('click', () => {
diff --git a/tools/visual_debugger/server.py b/tools/visual_debugger/server.py
index 4f9206ae..f5e8a56 100755
--- a/tools/visual_debugger/server.py
+++ b/tools/visual_debugger/server.py
@@ -3,6 +3,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import json
 import sys
 
 if (sys.version_info < (3, )):
@@ -23,29 +24,31 @@
 
 class CORSRequestHandler(SimpleHTTPRequestHandler):
   def do_GET(self):
-    if (self.path == "/discover.html"):
+    if (self.path == "/discover.json"):
+      remote_discovery_url = "http://localhost:{remote_port}/json/version".format(
+          remote_port=remote_port)
       try:
-        contents = urllib.request.urlopen("http://localhost:" +
-                                          str(remote_port) +
-                                          "/json/version").read()
-        self.send_response(200)
+        contents = urllib.request.urlopen(remote_discovery_url).read()
 
       except Exception:
-        contents =\
-        "\n Cannot connect to remote discovery page on localhost:"+\
-             str(remote_port) +\
-            "\n check for target command line parameter: \n" +\
-            "        --remote-debugging-port=" + str(remote_port) +\
-            "\n and if the target is a remote DUT  tunnel forwarding"+\
-              " is required from local to remote : " + \
-            "\n      ssh root@$DUT_IP -L " + \
-            str(remote_port)+":localhost:" + str(remote_port)
+        output = {
+            "error":
+            '''\
+Cannot connect to remote discovery page on:
+        {remote_discovery_url}
+Check for target command line parameter:
+        --remote-debugging-port={remote_port}
+If the target is a remote DUT tunnel forwarding is required
+from local to remote:
+        ssh root@$DUT_IP -L {remote_port}:localhost:{remote_port}
+            '''.format(remote_port=remote_port,
+                       remote_discovery_url=remote_discovery_url)
+        }
+        contents = json.dumps(output)
         contents = bytes(contents, 'UTF-8')
-        # Used error code 206 to prevent console logs every time
-        # connection is unsuccessful.
-        self.send_response(206)
 
-      self.send_header("Content-type", "text/html")
+      self.send_response(200)
+      self.send_header("Content-type", "application/json")
       self.send_header("Content-length", len(contents))
       self.end_headers()
       self.wfile.write(contents)
diff --git a/tools/vscode/settings.json b/tools/vscode/settings.json
index c0a3f54..98488b95 100644
--- a/tools/vscode/settings.json
+++ b/tools/vscode/settings.json
@@ -11,6 +11,16 @@
   "editor.detectIndentation": false,
   // Add a line at 80 characters.
   "editor.rulers": [80],
+
+  "extensions": {
+    "recommendations": [
+      // The GN language server from Microsoft.
+      //
+      // Provides support for .gn and .gni files.
+      "msedge-dev.gnls",
+    ],
+  },
+
   // Forces LF instead of "auto" which uses CRLF on Windows.
   "files.eol": "\n",
   // Trim tailing whitespace on save.
@@ -22,12 +32,6 @@
   "files.associations": {
     // Adds xml syntax highlighting for grd files.
     "*.grd" : "xml",
-    // Optional: .gn and .gni are not JavaScript, but at least it gives some
-    // approximate syntax highlighting. Ignore the linter warnings!
-    // There's an extension for these files, excluding the linter headaches.
-    // https://marketplace.visualstudio.com/items?itemName=npclaudiu.vscode-gn
-    "*.gni" : "javascript",
-    "*.gn" : "javascript"
   },
 
   "files.exclude": {
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/DisableAnimationsTestRule.java b/ui/android/javatests/src/org/chromium/ui/test/util/DisableAnimationsTestRule.java
index 6392e49..6819ca9 100644
--- a/ui/android/javatests/src/org/chromium/ui/test/util/DisableAnimationsTestRule.java
+++ b/ui/android/javatests/src/org/chromium/ui/test/util/DisableAnimationsTestRule.java
@@ -7,6 +7,7 @@
 import android.os.Build;
 import android.os.IBinder;
 import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -115,7 +116,8 @@
                             "settings put global transition_animation_scale " + scaleFactor,
                             "settings put global window_animation_scale " + scaleFactor);
             for (String command : commandToRuns) {
-                Runtime.getRuntime().exec(command);
+                InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+                        command);
             }
         } else {
             // Set animation scales through reflection in R-.
diff --git a/ui/base/ime/fuchsia/BUILD.gn b/ui/base/ime/fuchsia/BUILD.gn
index 1fc9873f..db19f4e 100644
--- a/ui/base/ime/fuchsia/BUILD.gn
+++ b/ui/base/ime/fuchsia/BUILD.gn
@@ -22,7 +22,7 @@
     "//base",
     "//third_party/abseil-cpp:absl",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.input.virtualkeyboard:fuchsia.input.virtualkeyboard_hlcpp",
-    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3:fuchsia.ui.input3_cpp",
+    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3:fuchsia.ui.input3_hlcpp",
     "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
     "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
     "//ui/base/ime",
diff --git a/ui/base/ime/fuchsia/keyboard_client.cc b/ui/base/ime/fuchsia/keyboard_client.cc
index fa04d03..4ee5911 100644
--- a/ui/base/ime/fuchsia/keyboard_client.cc
+++ b/ui/base/ime/fuchsia/keyboard_client.cc
@@ -4,15 +4,13 @@
 
 #include "ui/base/ime/fuchsia/keyboard_client.h"
 
-#include <lib/async/default.h>
-
 #include <limits>
 #include <tuple>
 #include <utility>
 
-#include "base/fuchsia/fuchsia_logging.h"
 #include "base/logging.h"
 #include "base/notreached.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/events/event.h"
 #include "ui/events/fuchsia/input_event_sink.h"
 #include "ui/events/keycodes/dom/dom_code.h"
@@ -26,10 +24,10 @@
 
 // Adds `flag` to `event_flags` if `modifier` is present. Also removes handled
 // modifiers from `unhandled_modifiers`.
-inline void MaybeAddFlag(fuchsia_ui_input3::Modifiers modifier,
+inline void MaybeAddFlag(fuchsia::ui::input3::Modifiers modifier,
                          EventFlags flag,
                          EventFlags& event_flags,
-                         fuchsia_ui_input3::Modifiers& unhandled_modifiers) {
+                         fuchsia::ui::input3::Modifiers& unhandled_modifiers) {
   if (unhandled_modifiers & modifier) {
     event_flags |= flag;
     // Remove modifier from unhandled.
@@ -39,72 +37,72 @@
 
 // Converts the state of modifiers managed by Fuchsia (e.g. Caps and Num Lock)
 // into ui::Event flags.
-int ModifiersToEventFlags(fuchsia_ui_input3::Modifiers modifiers) {
+int ModifiersToEventFlags(fuchsia::ui::input3::Modifiers modifiers) {
   EventFlags event_flags = EF_NONE;
-  MaybeAddFlag(fuchsia_ui_input3::Modifiers::kCapsLock, EF_CAPS_LOCK_ON,
+  MaybeAddFlag(fuchsia::ui::input3::Modifiers::CAPS_LOCK, EF_CAPS_LOCK_ON,
                event_flags, modifiers);
-  MaybeAddFlag(fuchsia_ui_input3::Modifiers::kNumLock, EF_NUM_LOCK_ON,
+  MaybeAddFlag(fuchsia::ui::input3::Modifiers::NUM_LOCK, EF_NUM_LOCK_ON,
                event_flags, modifiers);
-  MaybeAddFlag(fuchsia_ui_input3::Modifiers::kScrollLock, EF_SCROLL_LOCK_ON,
+  MaybeAddFlag(fuchsia::ui::input3::Modifiers::SCROLL_LOCK, EF_SCROLL_LOCK_ON,
                event_flags, modifiers);
 
   // This mapping is present in case blink adds support in the future, but blink
   // doesn't currently output the Function modifier. See
   // https://crsrc.org/c/ui/events/blink/blink_event_util.cc;l=268?q=EventFlagsToWebEventModifiers
-  MaybeAddFlag(fuchsia_ui_input3::Modifiers::kFunction, EF_FUNCTION_DOWN,
+  MaybeAddFlag(fuchsia::ui::input3::Modifiers::FUNCTION, EF_FUNCTION_DOWN,
                event_flags, modifiers);
-  if (modifiers & fuchsia_ui_input3::Modifiers::kSymbol) {
-    // fuchsia_ui_input3::Modifiers::SYMBOL has no equivalent in
+  if (modifiers & fuchsia::ui::input3::Modifiers::SYMBOL) {
+    // fuchsia::ui::input3::Modifiers::SYMBOL has no equivalent in
     // //ui/events/event_constants.h.
     DLOG(WARNING) << "Ignoring unsupported Symbol modifier.";
-    modifiers &= ~fuchsia_ui_input3::Modifiers::kSymbol;
+    modifiers &= ~fuchsia::ui::input3::Modifiers::SYMBOL;
   }
 
-  MaybeAddFlag(fuchsia_ui_input3::Modifiers::kShift, EF_SHIFT_DOWN, event_flags,
-               modifiers);
-  if (modifiers & (fuchsia_ui_input3::Modifiers::kLeftShift |
-                   fuchsia_ui_input3::Modifiers::kRightShift)) {
+  MaybeAddFlag(fuchsia::ui::input3::Modifiers::SHIFT, EF_SHIFT_DOWN,
+               event_flags, modifiers);
+  if (modifiers & (fuchsia::ui::input3::Modifiers::LEFT_SHIFT |
+                   fuchsia::ui::input3::Modifiers::RIGHT_SHIFT)) {
     DCHECK(event_flags & EF_SHIFT_DOWN)
         << "Fuchsia is expected to provide an agnostic SHIFT modifier for both "
            "LEFT and RIGHT SHIFT";
-    modifiers &= ~fuchsia_ui_input3::Modifiers::kLeftShift &
-                 ~fuchsia_ui_input3::Modifiers::kRightShift;
+    modifiers &= ~fuchsia::ui::input3::Modifiers::LEFT_SHIFT &
+                 ~fuchsia::ui::input3::Modifiers::RIGHT_SHIFT;
   }
 
-  MaybeAddFlag(fuchsia_ui_input3::Modifiers::kAlt, EF_ALT_DOWN, event_flags,
+  MaybeAddFlag(fuchsia::ui::input3::Modifiers::ALT, EF_ALT_DOWN, event_flags,
                modifiers);
-  if (modifiers & (fuchsia_ui_input3::Modifiers::kLeftAlt |
-                   fuchsia_ui_input3::Modifiers::kRightAlt)) {
+  if (modifiers & (fuchsia::ui::input3::Modifiers::LEFT_ALT |
+                   fuchsia::ui::input3::Modifiers::RIGHT_ALT)) {
     DCHECK(event_flags & EF_ALT_DOWN)
         << "Fuchsia is expected to provide an agnostic ALT modifier for both "
            "LEFT and RIGHT ALT";
-    modifiers &= ~fuchsia_ui_input3::Modifiers::kLeftAlt &
-                 ~fuchsia_ui_input3::Modifiers::kRightAlt;
+    modifiers &= ~fuchsia::ui::input3::Modifiers::LEFT_ALT &
+                 ~fuchsia::ui::input3::Modifiers::RIGHT_ALT;
   }
 
-  MaybeAddFlag(fuchsia_ui_input3::Modifiers::kAltGraph, EF_ALTGR_DOWN,
+  MaybeAddFlag(fuchsia::ui::input3::Modifiers::ALT_GRAPH, EF_ALTGR_DOWN,
                event_flags, modifiers);
 
-  MaybeAddFlag(fuchsia_ui_input3::Modifiers::kMeta, EF_COMMAND_DOWN,
+  MaybeAddFlag(fuchsia::ui::input3::Modifiers::META, EF_COMMAND_DOWN,
                event_flags, modifiers);
-  if (modifiers & (fuchsia_ui_input3::Modifiers::kLeftMeta |
-                   fuchsia_ui_input3::Modifiers::kRightMeta)) {
+  if (modifiers & (fuchsia::ui::input3::Modifiers::LEFT_META |
+                   fuchsia::ui::input3::Modifiers::RIGHT_META)) {
     DCHECK(event_flags & EF_COMMAND_DOWN)
         << "Fuchsia is expected to provide an agnostic META modifier for both "
            "LEFT and RIGHT META";
-    modifiers &= ~fuchsia_ui_input3::Modifiers::kLeftMeta &
-                 ~fuchsia_ui_input3::Modifiers::kRightMeta;
+    modifiers &= ~fuchsia::ui::input3::Modifiers::LEFT_META &
+                 ~fuchsia::ui::input3::Modifiers::RIGHT_META;
   }
 
-  MaybeAddFlag(fuchsia_ui_input3::Modifiers::kCtrl, EF_CONTROL_DOWN,
+  MaybeAddFlag(fuchsia::ui::input3::Modifiers::CTRL, EF_CONTROL_DOWN,
                event_flags, modifiers);
-  if (modifiers & (fuchsia_ui_input3::Modifiers::kLeftCtrl |
-                   fuchsia_ui_input3::Modifiers::kRightCtrl)) {
+  if (modifiers & (fuchsia::ui::input3::Modifiers::LEFT_CTRL |
+                   fuchsia::ui::input3::Modifiers::RIGHT_CTRL)) {
     DCHECK(event_flags & EF_CONTROL_DOWN)
         << "Fuchsia is expected to provide an agnostic CTRL modifier for both "
            "LEFT and RIGHT CTRL";
-    modifiers &= ~fuchsia_ui_input3::Modifiers::kLeftCtrl &
-                 ~fuchsia_ui_input3::Modifiers::kRightCtrl;
+    modifiers &= ~fuchsia::ui::input3::Modifiers::LEFT_CTRL &
+                 ~fuchsia::ui::input3::Modifiers::RIGHT_CTRL;
   }
 
   DLOG_IF(WARNING, modifiers)
@@ -113,14 +111,14 @@
 }
 
 absl::optional<EventType> ConvertKeyEventType(
-    fuchsia_ui_input3::KeyEventType type) {
+    fuchsia::ui::input3::KeyEventType type) {
   switch (type) {
-    case fuchsia_ui_input3::KeyEventType::kPressed:
+    case fuchsia::ui::input3::KeyEventType::PRESSED:
       return ET_KEY_PRESSED;
-    case fuchsia_ui_input3::KeyEventType::kReleased:
+    case fuchsia::ui::input3::KeyEventType::RELEASED:
       return ET_KEY_RELEASED;
-    case fuchsia_ui_input3::KeyEventType::kSync:
-    case fuchsia_ui_input3::KeyEventType::kCancel:
+    case fuchsia::ui::input3::KeyEventType::SYNC:
+    case fuchsia::ui::input3::KeyEventType::CANCEL:
       // SYNC and CANCEL should not generate ui::Events.
       return absl::nullopt;
     default:
@@ -132,84 +130,73 @@
 
 }  // namespace
 
-KeyboardClient::KeyboardClient(
-    fidl::Client<fuchsia_ui_input3::Keyboard>& keyboard_fidl_client,
-    fuchsia_ui_views::ViewRef view_ref,
-    InputEventSink* event_sink)
-    : event_sink_(event_sink) {
+KeyboardClient::KeyboardClient(fuchsia::ui::input3::Keyboard* keyboard_service,
+                               fuchsia::ui::views::ViewRef view_ref,
+                               InputEventSink* event_sink)
+    : binding_(this), event_sink_(event_sink) {
   DCHECK(event_sink_);
 
-  // Connect to the Keyboard service and register `keyboard_client_` as a
+  // Connect to the Keyboard service and register |keyboard_client_| as a
   // listener.
-  auto keyboard_listener_endpoints =
-      fidl::CreateEndpoints<fuchsia_ui_input3::KeyboardListener>();
-  ZX_CHECK(keyboard_listener_endpoints.is_ok(),
-           keyboard_listener_endpoints.status_value());
-  keyboard_fidl_client
-      ->AddListener(
-          {{.view_ref = std::move(view_ref),
-            .listener = std::move(keyboard_listener_endpoints->client)}})
-      .ThenExactlyOnce([](auto result) {});
-  binding_.emplace(async_get_default_dispatcher(),
-                   std::move(keyboard_listener_endpoints->server), this,
-                   fidl::kIgnoreBindingClosure);
+  fidl::InterfaceHandle<fuchsia::ui::input3::KeyboardListener>
+      keyboard_listener;
+  fidl::InterfaceRequest<fuchsia::ui::input3::KeyboardListener>
+      keyboard_listener_request = keyboard_listener.NewRequest();
+  keyboard_service->AddListener(std::move(view_ref),
+                                std::move(keyboard_listener), [] {});
+  binding_.Bind(std::move(keyboard_listener_request));
 }
 
 KeyboardClient::~KeyboardClient() = default;
 
 void KeyboardClient::OnKeyEvent(
-    KeyboardClient::OnKeyEventRequest& request,
-    KeyboardClient::OnKeyEventCompleter::Sync& completer) {
-  if (!IsValid(request.event())) {
-    binding_->Close(ZX_ERR_INVALID_ARGS);
+    fuchsia::ui::input3::KeyEvent key_event,
+    fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback callback) {
+  if (!IsValid(key_event)) {
+    binding_.Close(ZX_ERR_INVALID_ARGS);
     return;
   }
 
-  if (ProcessKeyEvent(request.event())) {
-    completer.Reply(fuchsia_ui_input3::KeyEventStatus::kHandled);
+  if (ProcessKeyEvent(key_event)) {
+    callback(fuchsia::ui::input3::KeyEventStatus::HANDLED);
   } else {
-    completer.Reply(fuchsia_ui_input3::KeyEventStatus::kNotHandled);
+    callback(fuchsia::ui::input3::KeyEventStatus::NOT_HANDLED);
   }
 }
 
-bool KeyboardClient::IsValid(const fuchsia_ui_input3::KeyEvent& key_event) {
-  if (!key_event.type() || !key_event.timestamp()) {
+bool KeyboardClient::IsValid(const fuchsia::ui::input3::KeyEvent& key_event) {
+  if (!key_event.has_type() || !key_event.has_timestamp())
     return false;
-  }
 
-  if (!key_event.key() && !key_event.key_meaning()) {
+  if (!key_event.has_key() && !key_event.has_key_meaning())
     return false;
-  }
 
   return true;
 }
 
 bool KeyboardClient::ProcessKeyEvent(
-    const fuchsia_ui_input3::KeyEvent& key_event) {
-  absl::optional<EventType> event_type =
-      ConvertKeyEventType(key_event.type().value());
+    const fuchsia::ui::input3::KeyEvent& key_event) {
+  absl::optional<EventType> event_type = ConvertKeyEventType(key_event.type());
   if (!event_type)
     return false;
 
-  // Convert `key_event` to a ui::KeyEvent.
+  // Convert |key_event| to a ui::KeyEvent.
   int event_flags = EF_NONE;
-  if (key_event.modifiers()) {
-    event_flags |= ModifiersToEventFlags(key_event.modifiers().value());
-  }
-  if (key_event.repeat_sequence()) {
+  if (key_event.has_modifiers())
+    event_flags |= ModifiersToEventFlags(key_event.modifiers());
+  if (key_event.has_repeat_sequence()) {
     event_flags |= EF_IS_REPEAT;
   }
 
   // Derive the DOM Key and Code directly from the event's fields.
-  // `key_event` has already been validated, so is guaranteed to have one
-  // or both of the `key` or `key_meaning` fields set.
+  // |key_event| has already been validated, so is guaranteed to have one
+  // or both of the |key| or |key_meaning| fields set.
   DomCode dom_code = DomCode::NONE;
   DomKey dom_key = DomKey::UNIDENTIFIED;
   KeyboardCode key_code = VKEY_UNKNOWN;
 
-  if (key_event.key()) {
-    dom_code = KeycodeConverter::UsbKeycodeToDomCode(
-        static_cast<uint32_t>(key_event.key().value()));
+  if (key_event.has_key()) {
+    dom_code = KeycodeConverter::UsbKeycodeToDomCode(key_event.key());
 
     // Derive the legacy key_code. At present this only takes into account the
     // DOM Code, and event flags, so requires that key() be set.
@@ -221,26 +208,26 @@
         DomCodeToUsLayoutDomKey(dom_code, event_flags, &dom_key, &key_code);
   }
 
-  if (key_event.key_meaning()) {
+  if (key_event.has_key_meaning()) {
     // If the KeyMeaning is specified then use it to set the DOM Key.
 
     // Ignore events with codepoints outside the Basic Multilingual Plane,
     // since the Chromium keyboard pipeline cannot currently handle them.
-    if (key_event.key_meaning()->codepoint() &&
-        (key_event.key_meaning()->codepoint().value() >
+    if (key_event.key_meaning().is_codepoint() &&
+        (key_event.key_meaning().codepoint() >
          std::numeric_limits<char16_t>::max())) {
       return false;
     }
 
     DomKey dom_key_from_meaning =
-        DomKeyFromFuchsiaKeyMeaning(key_event.key_meaning().value());
+        DomKeyFromFuchsiaKeyMeaning(key_event.key_meaning());
     if (dom_key_from_meaning != DomKey::UNIDENTIFIED)
       dom_key = dom_key_from_meaning;
   }
 
   ui::KeyEvent converted_event(
       *event_type, key_code, dom_code, event_flags, dom_key,
-      base::TimeTicks::FromZxTime(key_event.timestamp().value()));
+      base::TimeTicks::FromZxTime(key_event.timestamp()));
   event_sink_->DispatchEvent(&converted_event);
   return converted_event.handled();
 }
diff --git a/ui/base/ime/fuchsia/keyboard_client.h b/ui/base/ime/fuchsia/keyboard_client.h
index ab07857..8e27ead8 100644
--- a/ui/base/ime/fuchsia/keyboard_client.h
+++ b/ui/base/ime/fuchsia/keyboard_client.h
@@ -5,11 +5,10 @@
 #ifndef UI_BASE_IME_FUCHSIA_KEYBOARD_CLIENT_H_
 #define UI_BASE_IME_FUCHSIA_KEYBOARD_CLIENT_H_
 
-#include <fidl/fuchsia.ui.input3/cpp/fidl.h>
+#include <fuchsia/ui/input3/cpp/fidl.h>
 #include <lib/fidl/cpp/binding.h>
 
 #include "base/component_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ui {
 
@@ -17,41 +16,40 @@
 
 // Handles keyboard events from the Fuchsia keyboard service.
 class COMPONENT_EXPORT(UI_BASE_IME_FUCHSIA) KeyboardClient
-    : public fidl::Server<fuchsia_ui_input3::KeyboardListener> {
+    : public fuchsia::ui::input3::KeyboardListener {
  public:
-  // `keyboard_service` and `event_sink` must outlive `this`.
-  KeyboardClient(
-      fidl::Client<fuchsia_ui_input3::Keyboard>& keyboard_fidl_client,
-      fuchsia_ui_views::ViewRef view_ref,
-      InputEventSink* event_sink);
+  // |keyboard_service| and |event_sink| must outlive |this|.
+  KeyboardClient(fuchsia::ui::input3::Keyboard* keyboard_service,
+                 fuchsia::ui::views::ViewRef view_ref,
+                 InputEventSink* event_sink);
   ~KeyboardClient() override;
 
   KeyboardClient(const KeyboardClient&) = delete;
   KeyboardClient& operator=(const KeyboardClient&) = delete;
 
-  // fuchsia_ui_input3::KeyboardListener implementation.
-  void OnKeyEvent(OnKeyEventRequest& request,
-                  OnKeyEventCompleter::Sync& completer) final;
+  // fuchsia::ui::input3::KeyboardListener implementation.
+  void OnKeyEvent(
+      fuchsia::ui::input3::KeyEvent key_event,
+      fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback callback) final;
 
  private:
-  bool IsValid(const fuchsia_ui_input3::KeyEvent& key_event);
+  bool IsValid(const fuchsia::ui::input3::KeyEvent& key_event);
 
-  // Handles converting and propagating `key_event`. Returns false if critical
-  // information about `key_event` is missing, or if the key's event type is not
+  // Handles converting and propagating |key_event|. Returns false if critical
+  // information about |key_event| is missing, or if the key's event type is not
   // supported.
   // TODO(http://fxbug.dev/69620): Add support for SYNC and CANCEL key event
   // types.
-  bool ProcessKeyEvent(const fuchsia_ui_input3::KeyEvent& key_event);
+  bool ProcessKeyEvent(const fuchsia::ui::input3::KeyEvent& key_event);
 
   // Update the value of modifiers such as shift.
-  void UpdateCachedModifiers(const fuchsia_ui_input3::KeyEvent& key_event);
+  void UpdateCachedModifiers(const fuchsia::ui::input3::KeyEvent& key_event);
 
   // Translate state of locally tracked modifier keys (e.g. shift, alt) into
   // ui::Event flags.
   int EventFlagsForCachedModifiers();
 
-  absl::optional<fidl::ServerBinding<fuchsia_ui_input3::KeyboardListener>>
-      binding_;
+  fidl::Binding<fuchsia::ui::input3::KeyboardListener> binding_;
 
   // Dispatches events into Chromium once they have been converted to
   // ui::KeyEvents.
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index 7a99006..af2d65ca 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -166,7 +166,7 @@
       "keycodes/keyboard_code_conversion_fuchsia.h",
     ]
 
-    deps += [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3:fuchsia.ui.input3_cpp" ]
+    deps += [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3:fuchsia.ui.input3_hlcpp" ]
   }
 
   if (ozone_platform_x11) {
@@ -734,7 +734,7 @@
 
       deps += [
         "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input:fuchsia.ui.input_hlcpp",
-        "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3:fuchsia.ui.input3_cpp",
+        "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3:fuchsia.ui.input3_hlcpp",
         "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.pointer:fuchsia.ui.pointer_cpp",
         "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.pointer:fuchsia.ui.pointer_cpp_hlcpp_conversion",
         "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp_testing",
diff --git a/ui/events/keycodes/keyboard_code_conversion_fuchsia.cc b/ui/events/keycodes/keyboard_code_conversion_fuchsia.cc
index 8cc7626..db086acf 100644
--- a/ui/events/keycodes/keyboard_code_conversion_fuchsia.cc
+++ b/ui/events/keycodes/keyboard_code_conversion_fuchsia.cc
@@ -4,7 +4,7 @@
 
 #include "ui/events/keycodes/keyboard_code_conversion_fuchsia.h"
 
-#include <fidl/fuchsia.ui.input3/cpp/fidl.h>
+#include <fuchsia/ui/input3/cpp/fidl.h>
 
 #include "base/containers/flat_map.h"
 
@@ -12,16 +12,13 @@
 namespace {
 
 DomKey DomKeyFromFuchsiaNonPrintableKey(
-    const fuchsia_ui_input3::NonPrintableKey& key) {
-  if (key == fuchsia_ui_input3::NonPrintableKey::kEnter) {
+    const fuchsia::ui::input3::NonPrintableKey& key) {
+  if (key == fuchsia::ui::input3::NonPrintableKey::ENTER)
     return DomKey::ENTER;
-  }
-  if (key == fuchsia_ui_input3::NonPrintableKey::kTab) {
+  if (key == fuchsia::ui::input3::NonPrintableKey::TAB)
     return DomKey::TAB;
-  }
-  if (key == fuchsia_ui_input3::NonPrintableKey::kBackspace) {
+  if (key == fuchsia::ui::input3::NonPrintableKey::BACKSPACE)
     return DomKey::BACKSPACE;
-  }
 
   return DomKey::UNIDENTIFIED;
 }
@@ -29,21 +26,18 @@
 }  // namespace
 
 DomKey DomKeyFromFuchsiaKeyMeaning(
-    const fuchsia_ui_input3::KeyMeaning& key_meaning) {
-  if (key_meaning.codepoint()) {
+    const fuchsia::ui::input3::KeyMeaning& key_meaning) {
+  if (key_meaning.is_codepoint()) {
     // TODO(fxbug.dev/106600): Remove this check for codepoint zero, once the
     // platform provides non-printable key meanings consistently.
-    if (key_meaning.codepoint().value() == 0) {
+    if (key_meaning.codepoint() == 0)
       return DomKey::UNIDENTIFIED;
-    }
 
-    return DomKey::FromCharacter(key_meaning.codepoint().value());
+    return DomKey::FromCharacter(key_meaning.codepoint());
   }
 
-  if (key_meaning.non_printable_key()) {
-    return DomKeyFromFuchsiaNonPrintableKey(
-        key_meaning.non_printable_key().value());
-  }
+  if (key_meaning.is_non_printable_key())
+    return DomKeyFromFuchsiaNonPrintableKey(key_meaning.non_printable_key());
 
   return DomKey::UNIDENTIFIED;
 }
diff --git a/ui/events/keycodes/keyboard_code_conversion_fuchsia.h b/ui/events/keycodes/keyboard_code_conversion_fuchsia.h
index 39891089..0e9d30c 100644
--- a/ui/events/keycodes/keyboard_code_conversion_fuchsia.h
+++ b/ui/events/keycodes/keyboard_code_conversion_fuchsia.h
@@ -8,15 +8,19 @@
 #include "ui/events/events_base_export.h"
 #include "ui/events/keycodes/dom/dom_key.h"
 
-namespace fuchsia_ui_input3 {
+namespace fuchsia {
+namespace ui {
+namespace input3 {
 class KeyMeaning;
-}  // namespace fuchsia_ui_input3
+}  // namespace input3
+}  // namespace ui
+}  // namespace fuchsia
 
 namespace ui {
 
 // Converts a Fuchsia KeyMeaning to a DomKey.
 EVENTS_BASE_EXPORT DomKey
-DomKeyFromFuchsiaKeyMeaning(const fuchsia_ui_input3::KeyMeaning& key_meaning);
+DomKeyFromFuchsiaKeyMeaning(const fuchsia::ui::input3::KeyMeaning& key_meaning);
 
 }  // namespace ui
 
diff --git a/ui/events/keycodes/keyboard_code_conversion_fuchsia_unittest.cc b/ui/events/keycodes/keyboard_code_conversion_fuchsia_unittest.cc
index 22821a9..f4e1db4 100644
--- a/ui/events/keycodes/keyboard_code_conversion_fuchsia_unittest.cc
+++ b/ui/events/keycodes/keyboard_code_conversion_fuchsia_unittest.cc
@@ -4,7 +4,8 @@
 
 #include "ui/events/keycodes/keyboard_code_conversion_fuchsia.h"
 
-#include <fidl/fuchsia.ui.input3/cpp/fidl.h>
+#include <fuchsia/input/cpp/fidl.h>
+#include <fuchsia/ui/input3/cpp/fidl.h>
 #include <cstdint>
 
 #include "base/logging.h"
@@ -14,9 +15,9 @@
 
 namespace {
 
-using fuchsia_input::Key;
-using fuchsia_ui_input3::KeyMeaning;
-using fuchsia_ui_input3::NonPrintableKey;
+using fuchsia::input::Key;
+using fuchsia::ui::input3::KeyMeaning;
+using fuchsia::ui::input3::NonPrintableKey;
 
 #define EXPECT_CODEPOINT_MAPS(codepoint)                                     \
   EXPECT_EQ(                                                                 \
@@ -59,8 +60,8 @@
 
   // Check that NonPrintableKeys are converted correctly.
   EXPECT_EQ(ui::DomKey::ENTER,
-            ui::DomKeyFromFuchsiaKeyMeaning(
-                KeyMeaning::WithNonPrintableKey(NonPrintableKey::kEnter)));
+            ui::DomKeyFromFuchsiaKeyMeaning(KeyMeaning::WithNonPrintableKey(
+                NonPrintableKey(NonPrintableKey::ENTER))));
 }
 
 }  // namespace
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 289827c0..9cf579f 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -720,6 +720,14 @@
                 'extensions': ['GL_ANGLE_shader_pixel_local_storage']}],
   'arguments': 'GLint plane, const GLuint* value', },
 { 'return_type': 'void',
+  'versions': [{'name': 'glFramebufferPixelLocalStorageInterruptANGLE',
+                'extensions': ['GL_ANGLE_shader_pixel_local_storage']}],
+  'arguments': '', },
+{ 'return_type': 'void',
+  'versions': [{'name': 'glFramebufferPixelLocalStorageRestoreANGLE',
+                'extensions': ['GL_ANGLE_shader_pixel_local_storage']}],
+  'arguments': '', },
+{ 'return_type': 'void',
   'names': ['glFramebufferRenderbufferEXT', 'glFramebufferRenderbuffer'],
   'arguments':
       'GLenum target, GLenum attachment, GLenum renderbuffertarget, '
diff --git a/ui/gl/gl_bindings_api_autogen_gl.h b/ui/gl/gl_bindings_api_autogen_gl.h
index 4a239d2..5047a12 100644
--- a/ui/gl/gl_bindings_api_autogen_gl.h
+++ b/ui/gl/gl_bindings_api_autogen_gl.h
@@ -432,6 +432,8 @@
                                                 const GLint* value) override;
 void glFramebufferPixelLocalClearValueuivANGLEFn(GLint plane,
                                                  const GLuint* value) override;
+void glFramebufferPixelLocalStorageInterruptANGLEFn() override;
+void glFramebufferPixelLocalStorageRestoreANGLEFn() override;
 void glFramebufferRenderbufferEXTFn(GLenum target,
                                     GLenum attachment,
                                     GLenum renderbuffertarget,
diff --git a/ui/gl/gl_bindings_autogen_gl.cc b/ui/gl/gl_bindings_autogen_gl.cc
index ce675c8b..50bee0d 100644
--- a/ui/gl/gl_bindings_autogen_gl.cc
+++ b/ui/gl/gl_bindings_autogen_gl.cc
@@ -1223,6 +1223,18 @@
             GetGLProcAddress("glFramebufferPixelLocalClearValueuivANGLE"));
   }
 
+  if (ext.b_GL_ANGLE_shader_pixel_local_storage) {
+    fn.glFramebufferPixelLocalStorageInterruptANGLEFn =
+        reinterpret_cast<glFramebufferPixelLocalStorageInterruptANGLEProc>(
+            GetGLProcAddress("glFramebufferPixelLocalStorageInterruptANGLE"));
+  }
+
+  if (ext.b_GL_ANGLE_shader_pixel_local_storage) {
+    fn.glFramebufferPixelLocalStorageRestoreANGLEFn =
+        reinterpret_cast<glFramebufferPixelLocalStorageRestoreANGLEProc>(
+            GetGLProcAddress("glFramebufferPixelLocalStorageRestoreANGLE"));
+  }
+
   if (ver->IsAtLeastGL(3u, 0u) || ver->is_es) {
     fn.glFramebufferRenderbufferEXTFn =
         reinterpret_cast<glFramebufferRenderbufferEXTProc>(
@@ -4024,6 +4036,14 @@
   driver_->fn.glFramebufferPixelLocalClearValueuivANGLEFn(plane, value);
 }
 
+void GLApiBase::glFramebufferPixelLocalStorageInterruptANGLEFn() {
+  driver_->fn.glFramebufferPixelLocalStorageInterruptANGLEFn();
+}
+
+void GLApiBase::glFramebufferPixelLocalStorageRestoreANGLEFn() {
+  driver_->fn.glFramebufferPixelLocalStorageRestoreANGLEFn();
+}
+
 void GLApiBase::glFramebufferRenderbufferEXTFn(GLenum target,
                                                GLenum attachment,
                                                GLenum renderbuffertarget,
@@ -7650,6 +7670,18 @@
   gl_api_->glFramebufferPixelLocalClearValueuivANGLEFn(plane, value);
 }
 
+void TraceGLApi::glFramebufferPixelLocalStorageInterruptANGLEFn() {
+  TRACE_EVENT_BINARY_EFFICIENT0(
+      "gpu", "TraceGLAPI::glFramebufferPixelLocalStorageInterruptANGLE");
+  gl_api_->glFramebufferPixelLocalStorageInterruptANGLEFn();
+}
+
+void TraceGLApi::glFramebufferPixelLocalStorageRestoreANGLEFn() {
+  TRACE_EVENT_BINARY_EFFICIENT0(
+      "gpu", "TraceGLAPI::glFramebufferPixelLocalStorageRestoreANGLE");
+  gl_api_->glFramebufferPixelLocalStorageRestoreANGLEFn();
+}
+
 void TraceGLApi::glFramebufferRenderbufferEXTFn(GLenum target,
                                                 GLenum attachment,
                                                 GLenum renderbuffertarget,
@@ -12046,6 +12078,20 @@
   gl_api_->glFramebufferPixelLocalClearValueuivANGLEFn(plane, value);
 }
 
+void LogGLApi::glFramebufferPixelLocalStorageInterruptANGLEFn() {
+  GL_SERVICE_LOG("glFramebufferPixelLocalStorageInterruptANGLE"
+                 << "("
+                 << ")");
+  gl_api_->glFramebufferPixelLocalStorageInterruptANGLEFn();
+}
+
+void LogGLApi::glFramebufferPixelLocalStorageRestoreANGLEFn() {
+  GL_SERVICE_LOG("glFramebufferPixelLocalStorageRestoreANGLE"
+                 << "("
+                 << ")");
+  gl_api_->glFramebufferPixelLocalStorageRestoreANGLEFn();
+}
+
 void LogGLApi::glFramebufferRenderbufferEXTFn(GLenum target,
                                               GLenum attachment,
                                               GLenum renderbuffertarget,
@@ -16880,6 +16926,14 @@
   NoContextHelper("glFramebufferPixelLocalClearValueuivANGLE");
 }
 
+void NoContextGLApi::glFramebufferPixelLocalStorageInterruptANGLEFn() {
+  NoContextHelper("glFramebufferPixelLocalStorageInterruptANGLE");
+}
+
+void NoContextGLApi::glFramebufferPixelLocalStorageRestoreANGLEFn() {
+  NoContextHelper("glFramebufferPixelLocalStorageRestoreANGLE");
+}
+
 void NoContextGLApi::glFramebufferRenderbufferEXTFn(GLenum target,
                                                     GLenum attachment,
                                                     GLenum renderbuffertarget,
diff --git a/ui/gl/gl_bindings_autogen_gl.h b/ui/gl/gl_bindings_autogen_gl.h
index 7f6fac81..b692284 100644
--- a/ui/gl/gl_bindings_autogen_gl.h
+++ b/ui/gl/gl_bindings_autogen_gl.h
@@ -507,6 +507,9 @@
 typedef void(GL_BINDING_CALL* glFramebufferPixelLocalClearValueuivANGLEProc)(
     GLint plane,
     const GLuint* value);
+typedef void(
+    GL_BINDING_CALL* glFramebufferPixelLocalStorageInterruptANGLEProc)();
+typedef void(GL_BINDING_CALL* glFramebufferPixelLocalStorageRestoreANGLEProc)();
 typedef void(GL_BINDING_CALL* glFramebufferRenderbufferEXTProc)(
     GLenum target,
     GLenum attachment,
@@ -2268,6 +2271,10 @@
       glFramebufferPixelLocalClearValueivANGLEFn;
   glFramebufferPixelLocalClearValueuivANGLEProc
       glFramebufferPixelLocalClearValueuivANGLEFn;
+  glFramebufferPixelLocalStorageInterruptANGLEProc
+      glFramebufferPixelLocalStorageInterruptANGLEFn;
+  glFramebufferPixelLocalStorageRestoreANGLEProc
+      glFramebufferPixelLocalStorageRestoreANGLEFn;
   glFramebufferRenderbufferEXTProc glFramebufferRenderbufferEXTFn;
   glFramebufferTexture2DEXTProc glFramebufferTexture2DEXTFn;
   glFramebufferTexture2DMultisampleEXTProc
@@ -3104,6 +3111,8 @@
   virtual void glFramebufferPixelLocalClearValueuivANGLEFn(
       GLint plane,
       const GLuint* value) = 0;
+  virtual void glFramebufferPixelLocalStorageInterruptANGLEFn() = 0;
+  virtual void glFramebufferPixelLocalStorageRestoreANGLEFn() = 0;
   virtual void glFramebufferRenderbufferEXTFn(GLenum target,
                                               GLenum attachment,
                                               GLenum renderbuffertarget,
@@ -4654,6 +4663,10 @@
   ::gl::g_current_gl_context->glFramebufferPixelLocalClearValueivANGLEFn
 #define glFramebufferPixelLocalClearValueuivANGLE \
   ::gl::g_current_gl_context->glFramebufferPixelLocalClearValueuivANGLEFn
+#define glFramebufferPixelLocalStorageInterruptANGLE \
+  ::gl::g_current_gl_context->glFramebufferPixelLocalStorageInterruptANGLEFn
+#define glFramebufferPixelLocalStorageRestoreANGLE \
+  ::gl::g_current_gl_context->glFramebufferPixelLocalStorageRestoreANGLEFn
 #define glFramebufferRenderbufferEXT \
   ::gl::g_current_gl_context->glFramebufferRenderbufferEXTFn
 #define glFramebufferTexture2DEXT \
diff --git a/ui/gl/gl_bindings_autogen_mock.cc b/ui/gl/gl_bindings_autogen_mock.cc
index b1d52585..0fcfa80 100644
--- a/ui/gl/gl_bindings_autogen_mock.cc
+++ b/ui/gl/gl_bindings_autogen_mock.cc
@@ -1631,6 +1631,18 @@
 }
 
 void GL_BINDING_CALL
+MockGLInterface::Mock_glFramebufferPixelLocalStorageInterruptANGLE() {
+  MakeGlMockFunctionUnique("glFramebufferPixelLocalStorageInterruptANGLE");
+  interface_->FramebufferPixelLocalStorageInterruptANGLE();
+}
+
+void GL_BINDING_CALL
+MockGLInterface::Mock_glFramebufferPixelLocalStorageRestoreANGLE() {
+  MakeGlMockFunctionUnique("glFramebufferPixelLocalStorageRestoreANGLE");
+  interface_->FramebufferPixelLocalStorageRestoreANGLE();
+}
+
+void GL_BINDING_CALL
 MockGLInterface::Mock_glFramebufferRenderbuffer(GLenum target,
                                                 GLenum attachment,
                                                 GLenum renderbuffertarget,
@@ -5974,6 +5986,12 @@
   if (strcmp(name, "glFramebufferPixelLocalClearValueuivANGLE") == 0)
     return reinterpret_cast<GLFunctionPointerType>(
         Mock_glFramebufferPixelLocalClearValueuivANGLE);
+  if (strcmp(name, "glFramebufferPixelLocalStorageInterruptANGLE") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glFramebufferPixelLocalStorageInterruptANGLE);
+  if (strcmp(name, "glFramebufferPixelLocalStorageRestoreANGLE") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(
+        Mock_glFramebufferPixelLocalStorageRestoreANGLE);
   if (strcmp(name, "glFramebufferRenderbuffer") == 0)
     return reinterpret_cast<GLFunctionPointerType>(
         Mock_glFramebufferRenderbuffer);
diff --git a/ui/gl/gl_bindings_autogen_mock.h b/ui/gl/gl_bindings_autogen_mock.h
index 6fd6e1c2..bdc33cd 100644
--- a/ui/gl/gl_bindings_autogen_mock.h
+++ b/ui/gl/gl_bindings_autogen_mock.h
@@ -685,6 +685,8 @@
 static void GL_BINDING_CALL
 Mock_glFramebufferPixelLocalClearValueuivANGLE(GLint plane,
                                                const GLuint* value);
+static void GL_BINDING_CALL Mock_glFramebufferPixelLocalStorageInterruptANGLE();
+static void GL_BINDING_CALL Mock_glFramebufferPixelLocalStorageRestoreANGLE();
 static void GL_BINDING_CALL
 Mock_glFramebufferRenderbuffer(GLenum target,
                                GLenum attachment,
diff --git a/ui/gl/gl_mock_autogen_gl.h b/ui/gl/gl_mock_autogen_gl.h
index a5ebd53..9997b61 100644
--- a/ui/gl/gl_mock_autogen_gl.h
+++ b/ui/gl/gl_mock_autogen_gl.h
@@ -391,6 +391,8 @@
              void(GLint plane, const GLint* value));
 MOCK_METHOD2(FramebufferPixelLocalClearValueuivANGLE,
              void(GLint plane, const GLuint* value));
+MOCK_METHOD0(FramebufferPixelLocalStorageInterruptANGLE, void());
+MOCK_METHOD0(FramebufferPixelLocalStorageRestoreANGLE, void());
 MOCK_METHOD4(FramebufferRenderbufferEXT,
              void(GLenum target,
                   GLenum attachment,
diff --git a/ui/gl/gl_stub_autogen_gl.h b/ui/gl/gl_stub_autogen_gl.h
index 6a581f5..4b63267 100644
--- a/ui/gl/gl_stub_autogen_gl.h
+++ b/ui/gl/gl_stub_autogen_gl.h
@@ -436,6 +436,8 @@
 void glFramebufferPixelLocalClearValueuivANGLEFn(GLint plane,
                                                  const GLuint* value) override {
 }
+void glFramebufferPixelLocalStorageInterruptANGLEFn() override {}
+void glFramebufferPixelLocalStorageRestoreANGLEFn() override {}
 void glFramebufferRenderbufferEXTFn(GLenum target,
                                     GLenum attachment,
                                     GLenum renderbuffertarget,
diff --git a/ui/ozone/platform/flatland/BUILD.gn b/ui/ozone/platform/flatland/BUILD.gn
index 8750b5a..ff5da9b 100644
--- a/ui/ozone/platform/flatland/BUILD.gn
+++ b/ui/ozone/platform/flatland/BUILD.gn
@@ -60,8 +60,6 @@
     "//third_party/angle/src/common/fuchsia_egl",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mem:fuchsia.mem_hlcpp",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem:fuchsia.sysmem_hlcpp",
-    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.views:fuchsia.ui.views_cpp_hlcpp_conversion",
-    "//third_party/fuchsia-sdk/sdk/pkg/component_incoming_cpp",
     "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
     "//ui/base",
     "//ui/base/cursor",
@@ -77,7 +75,7 @@
   public_deps = [
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.composition:fuchsia.ui.composition_hlcpp",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.gfx:fuchsia.ui.gfx_hlcpp",
-    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3:fuchsia.ui.input3_cpp",
+    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.input3:fuchsia.ui.input3_hlcpp",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.pointer:fuchsia.ui.pointer_cpp",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.pointer:fuchsia.ui.pointer_cpp_hlcpp_conversion",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.views:fuchsia.ui.views_hlcpp",
diff --git a/ui/ozone/platform/flatland/flatland_window.cc b/ui/ozone/platform/flatland/flatland_window.cc
index c6fbab7..c2ee8fc 100644
--- a/ui/ozone/platform/flatland/flatland_window.cc
+++ b/ui/ozone/platform/flatland/flatland_window.cc
@@ -5,9 +5,7 @@
 #include "ui/ozone/platform/flatland/flatland_window.h"
 
 #include <fidl/fuchsia.ui.pointer/cpp/hlcpp_conversion.h>
-#include <fidl/fuchsia.ui.views/cpp/hlcpp_conversion.h>
 #include <fuchsia/sys/cpp/fidl.h>
-#include <lib/async/default.h>
 #include <lib/sys/cpp/component_context.h>
 #include <lib/ui/scenic/cpp/view_identity.h>
 
@@ -19,8 +17,8 @@
 #include <vector>
 
 #include "base/check_op.h"
-#include "base/fuchsia/fuchsia_component_connect.h"
 #include "base/fuchsia/fuchsia_logging.h"
+#include "base/fuchsia/process_context.h"
 #include "base/functional/bind.h"
 #include "base/memory/scoped_refptr.h"
 #include "ui/base/cursor/platform_cursor.h"
@@ -123,15 +121,14 @@
 
   if (properties.enable_keyboard) {
     is_virtual_keyboard_enabled_ = properties.enable_virtual_keyboard;
-    auto keyboard_client_end =
-        base::fuchsia_component::Connect<fuchsia_ui_input3::Keyboard>();
-    CHECK(keyboard_client_end.is_ok())
-        << base::FidlConnectionErrorMessage(keyboard_client_end);
-    keyboard_fidl_client_.Bind(std::move(keyboard_client_end.value()),
-                               async_get_default_dispatcher(),
-                               &fidl_error_event_logger_);
-    keyboard_client_ = std::make_unique<KeyboardClient>(
-        keyboard_fidl_client_, fidl::HLCPPToNatural(CloneViewRef()), this);
+    keyboard_service_ = base::ComponentContextForProcess()
+                            ->svc()
+                            ->Connect<fuchsia::ui::input3::Keyboard>();
+    keyboard_service_.set_error_handler([](zx_status_t status) {
+      ZX_LOG(ERROR, status) << "input3.Keyboard service disconnected.";
+    });
+    keyboard_client_ = std::make_unique<KeyboardClient>(keyboard_service_.get(),
+                                                        CloneViewRef(), this);
   } else {
     DCHECK(!properties.enable_virtual_keyboard);
   }
diff --git a/ui/ozone/platform/flatland/flatland_window.h b/ui/ozone/platform/flatland/flatland_window.h
index 8c5382dc..acb7cc1 100644
--- a/ui/ozone/platform/flatland/flatland_window.h
+++ b/ui/ozone/platform/flatland/flatland_window.h
@@ -5,8 +5,8 @@
 #ifndef UI_OZONE_PLATFORM_FLATLAND_FLATLAND_WINDOW_H_
 #define UI_OZONE_PLATFORM_FLATLAND_FLATLAND_WINDOW_H_
 
-#include <fidl/fuchsia.ui.input3/cpp/fidl.h>
 #include <fuchsia/ui/composition/cpp/fidl.h>
+#include <fuchsia/ui/input3/cpp/fidl.h>
 #include <fuchsia/ui/views/cpp/fidl.h>
 #include <lib/ui/scenic/cpp/view_ref_pair.h>
 
@@ -15,7 +15,6 @@
 #include <vector>
 
 #include "base/component_export.h"
-#include "base/fuchsia/fidl_event_handler.h"
 #include "base/functional/callback.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/ime/fuchsia/keyboard_client.h"
@@ -122,9 +121,7 @@
   ScenicWindowDelegate* const scenic_window_delegate_;
   gfx::AcceleratedWidget const window_id_;
 
-  fidl::Client<fuchsia_ui_input3::Keyboard> keyboard_fidl_client_;
-  base::FidlErrorEventLogger<fuchsia_ui_input3::Keyboard>
-      fidl_error_event_logger_;
+  fuchsia::ui::input3::KeyboardPtr keyboard_service_;
   std::unique_ptr<KeyboardClient> keyboard_client_;
   std::unique_ptr<PointerEventsHandler> pointer_handler_;
 
diff --git a/ui/ozone/platform/scenic/BUILD.gn b/ui/ozone/platform/scenic/BUILD.gn
index 29e29e87..112607d 100644
--- a/ui/ozone/platform/scenic/BUILD.gn
+++ b/ui/ozone/platform/scenic/BUILD.gn
@@ -67,8 +67,6 @@
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mem:fuchsia.mem_hlcpp",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem:fuchsia.sysmem_hlcpp",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.pointer:fuchsia.ui.pointer_cpp_hlcpp_conversion",
-    "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.views:fuchsia.ui.views_cpp_hlcpp_conversion",
-    "//third_party/fuchsia-sdk/sdk/pkg/component_incoming_cpp",
     "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
     "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
     "//ui/base",
diff --git a/ui/ozone/platform/scenic/scenic_window.cc b/ui/ozone/platform/scenic/scenic_window.cc
index 1bfa940..c2e282e 100644
--- a/ui/ozone/platform/scenic/scenic_window.cc
+++ b/ui/ozone/platform/scenic/scenic_window.cc
@@ -5,9 +5,7 @@
 #include "ui/ozone/platform/scenic/scenic_window.h"
 
 #include <fidl/fuchsia.ui.pointer/cpp/hlcpp_conversion.h>
-#include <fidl/fuchsia.ui.views/cpp/hlcpp_conversion.h>
 #include <fuchsia/sys/cpp/fidl.h>
-#include <lib/async/default.h>
 #include <lib/sys/cpp/component_context.h>
 
 #include <algorithm>
@@ -17,8 +15,8 @@
 #include <utility>
 #include <vector>
 
-#include "base/fuchsia/fuchsia_component_connect.h"
 #include "base/fuchsia/fuchsia_logging.h"
+#include "base/fuchsia/process_context.h"
 #include "base/memory/scoped_refptr.h"
 #include "ui/base/cursor/platform_cursor.h"
 #include "ui/display/types/display_constants.h"
@@ -138,15 +136,14 @@
 
   if (properties.enable_keyboard) {
     is_virtual_keyboard_enabled_ = properties.enable_virtual_keyboard;
-    auto keyboard_client_end =
-        base::fuchsia_component::Connect<fuchsia_ui_input3::Keyboard>();
-    CHECK(keyboard_client_end.is_ok())
-        << base::FidlConnectionErrorMessage(keyboard_client_end);
-    keyboard_fidl_client_.Bind(std::move(keyboard_client_end.value()),
-                               async_get_default_dispatcher(),
-                               &fidl_error_event_logger_);
-    keyboard_client_ = std::make_unique<KeyboardClient>(
-        keyboard_fidl_client_, fidl::HLCPPToNatural(CloneViewRef()), this);
+    keyboard_service_ = base::ComponentContextForProcess()
+                            ->svc()
+                            ->Connect<fuchsia::ui::input3::Keyboard>();
+    keyboard_service_.set_error_handler([](zx_status_t status) {
+      ZX_LOG(ERROR, status) << "input3.Keyboard service disconnected.";
+    });
+    keyboard_client_ = std::make_unique<KeyboardClient>(keyboard_service_.get(),
+                                                        CloneViewRef(), this);
   } else {
     DCHECK(!properties.enable_virtual_keyboard);
   }
diff --git a/ui/ozone/platform/scenic/scenic_window.h b/ui/ozone/platform/scenic/scenic_window.h
index bac1e0a..9ad7099 100644
--- a/ui/ozone/platform/scenic/scenic_window.h
+++ b/ui/ozone/platform/scenic/scenic_window.h
@@ -5,10 +5,9 @@
 #ifndef UI_OZONE_PLATFORM_SCENIC_SCENIC_WINDOW_H_
 #define UI_OZONE_PLATFORM_SCENIC_SCENIC_WINDOW_H_
 
-#include <fidl/fuchsia.ui.input3/cpp/fidl.h>
 #include <fuchsia/ui/gfx/cpp/fidl.h>
 #include <fuchsia/ui/input/cpp/fidl.h>
-#include <fuchsia/ui/views/cpp/fidl.h>
+#include <fuchsia/ui/input3/cpp/fidl.h>
 #include <lib/ui/scenic/cpp/resources.h>
 #include <lib/ui/scenic/cpp/session.h>
 #include <lib/ui/scenic/cpp/view_ref_pair.h>
@@ -17,7 +16,6 @@
 #include <vector>
 
 #include "base/component_export.h"
-#include "base/fuchsia/fidl_event_handler.h"
 #include "ui/base/ime/fuchsia/keyboard_client.h"
 #include "ui/events/fuchsia/input_event_sink.h"
 #include "ui/events/fuchsia/pointer_events_handler.h"
@@ -147,9 +145,7 @@
   // Used to coordinate window closure requests with the shell.
   fuchsia::element::ViewControllerPtr view_controller_;
 
-  fidl::Client<fuchsia_ui_input3::Keyboard> keyboard_fidl_client_;
-  base::FidlErrorEventLogger<fuchsia_ui_input3::Keyboard>
-      fidl_error_event_logger_;
+  fuchsia::ui::input3::KeyboardPtr keyboard_service_;
   std::unique_ptr<KeyboardClient> keyboard_client_;
 
   // React to view-focus coming and going.
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc b/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
index 53f0907..ddb5816 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
@@ -408,11 +408,8 @@
 
 TEST_F(ViewAXPlatformNodeDelegateTest, OverrideNameAndDescription) {
   // Initially the button has no name and no description.
-  // TODO(accessibility): If nothing has set the name, should the NameFrom
-  // be kNone or kUninitialized instead of kAttribute?
   EXPECT_EQ(button_accessibility()->GetName(), "");
-  EXPECT_EQ(button_accessibility()->GetNameFrom(),
-            ax::mojom::NameFrom::kAttribute);
+  EXPECT_EQ(button_accessibility()->GetNameFrom(), ax::mojom::NameFrom::kNone);
   EXPECT_EQ(button_accessibility()->GetDescription(), "");
   EXPECT_EQ(button_accessibility()->GetDescriptionFrom(),
             ax::mojom::DescriptionFrom::kNone);
diff --git a/ui/views/accessible_pane_view.cc b/ui/views/accessible_pane_view.cc
index 696fade1..4bb6355 100644
--- a/ui/views/accessible_pane_view.cc
+++ b/ui/views/accessible_pane_view.cc
@@ -50,6 +50,7 @@
 AccessiblePaneView::AccessiblePaneView()
     : last_focused_view_tracker_(std::make_unique<ViewTracker>()) {
   focus_search_ = std::make_unique<AccessiblePaneViewFocusSearch>(this);
+  SetAccessibilityProperties(ax::mojom::Role::kPane);
 }
 
 AccessiblePaneView::~AccessiblePaneView() {
@@ -217,10 +218,6 @@
   View::SetVisible(flag);
 }
 
-void AccessiblePaneView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  node_data->role = ax::mojom::Role::kPane;
-}
-
 void AccessiblePaneView::RequestFocus() {
   SetPaneFocusAndFocusDefault();
 }
diff --git a/ui/views/accessible_pane_view.h b/ui/views/accessible_pane_view.h
index 8d03aa8..1bbb452 100644
--- a/ui/views/accessible_pane_view.h
+++ b/ui/views/accessible_pane_view.h
@@ -53,7 +53,6 @@
   FocusTraversable* GetPaneFocusTraversable() override;
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
   void SetVisible(bool flag) override;
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void RequestFocus() override;
 
   // Overridden from FocusChangeListener:
diff --git a/ui/views/accessible_pane_view_unittest.cc b/ui/views/accessible_pane_view_unittest.cc
index 24f348477..3d5d3cbf 100644
--- a/ui/views/accessible_pane_view_unittest.cc
+++ b/ui/views/accessible_pane_view_unittest.cc
@@ -269,4 +269,30 @@
   EXPECT_TRUE(test_view2->AcceleratorPressed(test_view2->escape_key()));
 }
 
+TEST_F(AccessiblePaneViewTest, AccessibleProperties) {
+  TestBarView* test_view = new TestBarView();
+  test_view->SetAccessibleName(u"Name");
+  test_view->SetAccessibleDescription(u"Description");
+  EXPECT_EQ(test_view->GetAccessibleName(), u"Name");
+  EXPECT_EQ(test_view->GetAccessibleDescription(), u"Description");
+  EXPECT_EQ(test_view->GetAccessibleRole(), ax::mojom::Role::kPane);
+
+  ui::AXNodeData data;
+  test_view->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
+            u"Name");
+  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kDescription),
+            u"Description");
+  EXPECT_EQ(data.role, ax::mojom::Role::kPane);
+
+  data = ui::AXNodeData();
+  test_view->SetAccessibleRole(ax::mojom::Role::kToolbar);
+  test_view->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
+            u"Name");
+  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kDescription),
+            u"Description");
+  EXPECT_EQ(data.role, ax::mojom::Role::kToolbar);
+}
+
 }  // namespace views
diff --git a/ui/views/controls/button/button.cc b/ui/views/controls/button/button.cc
index d50193d..61c61384 100644
--- a/ui/views/controls/button/button.cc
+++ b/ui/views/controls/button/button.cc
@@ -519,8 +519,7 @@
 }
 
 void Button::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  node_data->role = ax::mojom::Role::kButton;
-  node_data->SetName(GetAccessibleName());
+  View::GetAccessibleNodeData(node_data);
   if (!GetEnabled())
     node_data->SetRestriction(ax::mojom::Restriction::kDisabled);
 
@@ -606,6 +605,8 @@
   // TODO(pbos): Investigate not setting a default color so that we can DCHECK
   // if one hasn't been set.
   InkDrop::Get(this)->SetBaseColor(gfx::kPlaceholderColor);
+
+  SetAccessibilityProperties(ax::mojom::Role::kButton);
 }
 
 void Button::RequestFocusFromEvent() {
diff --git a/ui/views/controls/button/button_unittest.cc b/ui/views/controls/button/button_unittest.cc
index 2656d77..248bca8 100644
--- a/ui/views/controls/button/button_unittest.cc
+++ b/ui/views/controls/button/button_unittest.cc
@@ -927,4 +927,18 @@
   EXPECT_EQ(test_tooltip_text, base::ASCIIToUTF16(name));
 }
 
+TEST_F(ButtonTest, AccessibleRole) {
+  ui::AXNodeData data;
+  button()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kButton);
+  EXPECT_EQ(button()->GetAccessibleRole(), ax::mojom::Role::kButton);
+
+  button()->SetAccessibleRole(ax::mojom::Role::kCheckBox);
+
+  data = ui::AXNodeData();
+  button()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kCheckBox);
+  EXPECT_EQ(button()->GetAccessibleRole(), ax::mojom::Role::kCheckBox);
+}
+
 }  // namespace views
diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc
index ca06154..31682201 100644
--- a/ui/views/controls/button/label_button.cc
+++ b/ui/views/controls/button/label_button.cc
@@ -420,9 +420,10 @@
 }
 
 void LabelButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  if (GetIsDefault())
-    node_data->AddState(ax::mojom::State::kDefault);
   Button::GetAccessibleNodeData(node_data);
+  if (GetIsDefault()) {
+    node_data->AddState(ax::mojom::State::kDefault);
+  }
 }
 
 ui::NativeTheme::Part LabelButton::GetThemePart() const {
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index 37c22391..c619e78c 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -18,7 +18,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/accessibility/ax_node_data.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/base/cursor/cursor.h"
@@ -139,6 +138,11 @@
   ClearDisplayText();
   if (GetWidget())
     UpdateColorsFromTheme();
+
+  SetAccessibleRole(text_context_ == style::CONTEXT_DIALOG_TITLE
+                        ? ax::mojom::Role::kTitleBar
+                        : ax::mojom::Role::kStaticText);
+
   OnPropertyChanged(&text_context_, kPropertyEffectsPreferredSizeChanged);
 }
 
@@ -467,6 +471,7 @@
   if (tooltip_text_ == tooltip_text)
     return;
   tooltip_text_ = tooltip_text;
+  TooltipTextChanged();
   OnPropertyChanged(&tooltip_text_, kPropertyEffectsNone);
 }
 
@@ -715,15 +720,6 @@
   return this;
 }
 
-void Label::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  if (text_context_ == style::CONTEXT_DIALOG_TITLE)
-    node_data->role = ax::mojom::Role::kTitleBar;
-  else
-    node_data->role = ax::mojom::Role::kStaticText;
-
-  node_data->SetName(GetAccessibleName());
-}
-
 std::u16string Label::GetTooltipText(const gfx::Point& p) const {
   if (handles_tooltips_) {
     if (!tooltip_text_.empty())
@@ -1145,6 +1141,11 @@
   UpdateFullTextElideBehavior();
   full_text_->SetDirectionalityMode(directionality_mode);
 
+  SetAccessibilityProperties(text_context_ == style::CONTEXT_DIALOG_TITLE
+                                 ? ax::mojom::Role::kTitleBar
+                                 : ax::mojom::Role::kStaticText,
+                             text);
+
   SetText(text);
 
   // Only selectable labels will get requests to show the context menu, due to
diff --git a/ui/views/controls/label.h b/ui/views/controls/label.h
index 03c33a8..cdaa989 100644
--- a/ui/views/controls/label.h
+++ b/ui/views/controls/label.h
@@ -322,7 +322,6 @@
   View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
   bool GetCanProcessEventsWithinSubtree() const override;
   WordLookupClient* GetWordLookupClient() override;
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   std::u16string GetTooltipText(const gfx::Point& p) const override;
 
  protected:
diff --git a/ui/views/controls/label_unittest.cc b/ui/views/controls/label_unittest.cc
index 55dc466..36a5ce0 100644
--- a/ui/views/controls/label_unittest.cc
+++ b/ui/views/controls/label_unittest.cc
@@ -758,6 +758,39 @@
   EXPECT_EQ(u"Altered", label()->display_text_->GetDisplayText());
 }
 
+TEST_F(LabelTest, AccessibleNameAndRole) {
+  label()->SetText(u"Text");
+  EXPECT_EQ(label()->GetAccessibleName(), u"Text");
+  EXPECT_EQ(label()->GetAccessibleRole(), ax::mojom::Role::kStaticText);
+
+  ui::AXNodeData data;
+  label()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
+            u"Text");
+  EXPECT_EQ(data.role, ax::mojom::Role::kStaticText);
+
+  label()->SetTextContext(style::CONTEXT_DIALOG_TITLE);
+  EXPECT_EQ(label()->GetAccessibleName(), u"Text");
+  EXPECT_EQ(label()->GetAccessibleRole(), ax::mojom::Role::kTitleBar);
+
+  data = ui::AXNodeData();
+  label()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
+            u"Text");
+  EXPECT_EQ(data.role, ax::mojom::Role::kTitleBar);
+
+  label()->SetText(u"New Text");
+  label()->SetAccessibleRole(ax::mojom::Role::kLink);
+  EXPECT_EQ(label()->GetAccessibleName(), u"New Text");
+  EXPECT_EQ(label()->GetAccessibleRole(), ax::mojom::Role::kLink);
+
+  data = ui::AXNodeData();
+  label()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
+            u"New Text");
+  EXPECT_EQ(data.role, ax::mojom::Role::kLink);
+}
+
 TEST_F(LabelTest, EmptyLabelSizing) {
   const gfx::Size expected_size(0, label()->font_list().GetHeight());
   EXPECT_EQ(expected_size, label()->GetPreferredSize());
diff --git a/ui/views/controls/slider.cc b/ui/views/controls/slider.cc
index f8437b1e..b0b563e9 100644
--- a/ui/views/controls/slider.cc
+++ b/ui/views/controls/slider.cc
@@ -73,6 +73,7 @@
 Slider::Slider(SliderListener* listener) : listener_(listener) {
   highlight_animation_.SetSlideDuration(base::Milliseconds(150));
   SetFlipCanvasOnPaintForRTLUI(true);
+  SetAccessibilityProperties(ax::mojom::Role::kSlider);
 
 #if BUILDFLAG(IS_MAC)
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
@@ -321,7 +322,7 @@
 }
 
 void Slider::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  node_data->role = ax::mojom::Role::kSlider;
+  View::GetAccessibleNodeData(node_data);
   node_data->SetValue(base::UTF8ToUTF16(
       base::StringPrintf("%d%%", static_cast<int>(value_ * 100 + 0.5))));
   node_data->AddAction(ax::mojom::Action::kIncrement);
diff --git a/ui/views/controls/slider_unittest.cc b/ui/views/controls/slider_unittest.cc
index 8375a29e..4a7e59b 100644
--- a/ui/views/controls/slider_unittest.cc
+++ b/ui/views/controls/slider_unittest.cc
@@ -21,6 +21,7 @@
 #include "ui/events/gesture_event_details.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/test/event_generator.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/test/ax_event_counter.h"
 #include "ui/views/test/slider_test_api.h"
 #include "ui/views/test/views_test_base.h"
@@ -289,6 +290,20 @@
       slider()->GetValue());
 }
 
+TEST_P(SliderTest, AccessibleRole) {
+  ui::AXNodeData data;
+  slider()->GetViewAccessibility().GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kSlider);
+  EXPECT_EQ(slider()->GetAccessibleRole(), ax::mojom::Role::kSlider);
+
+  slider()->SetAccessibleRole(ax::mojom::Role::kMeter);
+
+  data = ui::AXNodeData();
+  slider()->GetViewAccessibility().GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kMeter);
+  EXPECT_EQ(slider()->GetAccessibleRole(), ax::mojom::Role::kMeter);
+}
+
 // No touch on desktop Mac. Tracked in http://crbug.com/445520.
 #if !BUILDFLAG(IS_MAC) || defined(USE_AURA)
 
diff --git a/ui/views/controls/styled_label.cc b/ui/views/controls/styled_label.cc
index de27c7f..5fc2b10 100644
--- a/ui/views/controls/styled_label.cc
+++ b/ui/views/controls/styled_label.cc
@@ -16,7 +16,6 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/accessibility/ax_node_data.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
@@ -79,7 +78,11 @@
   std::vector<std::unique_ptr<View>> owned_views;
 };
 
-StyledLabel::StyledLabel() = default;
+StyledLabel::StyledLabel() {
+  SetAccessibilityProperties(text_context_ == style::CONTEXT_DIALOG_TITLE
+                                 ? ax::mojom::Role::kTitleBar
+                                 : ax::mojom::Role::kStaticText);
+}
 
 StyledLabel::~StyledLabel() = default;
 
@@ -96,6 +99,7 @@
     return;
 
   text_ = text;
+  SetAccessibleName(text_);
   style_ranges_.clear();
   RemoveOrDeleteAllChildViews();
   OnPropertyChanged(&text_, kPropertyEffectsPreferredSizeChanged);
@@ -135,6 +139,9 @@
     return;
 
   text_context_ = text_context;
+  SetAccessibleRole(text_context_ == style::CONTEXT_DIALOG_TITLE
+                        ? ax::mojom::Role::kTitleBar
+                        : ax::mojom::Role::kStaticText);
   OnPropertyChanged(&text_context_, kPropertyEffectsPreferredSizeChanged);
 }
 
@@ -219,13 +226,6 @@
   SetSize(size);
 }
 
-void StyledLabel::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  node_data->role = (text_context_ == style::CONTEXT_DIALOG_TITLE)
-                        ? ax::mojom::Role::kTitleBar
-                        : ax::mojom::Role::kStaticText;
-  node_data->SetName(GetText());
-}
-
 gfx::Size StyledLabel::CalculatePreferredSize() const {
   // Respect any existing size.  If there is none, default to a single line.
   CalculateLayout((width() == 0) ? std::numeric_limits<int>::max() : width());
diff --git a/ui/views/controls/styled_label.h b/ui/views/controls/styled_label.h
index 646f67a..8884e55 100644
--- a/ui/views/controls/styled_label.h
+++ b/ui/views/controls/styled_label.h
@@ -171,7 +171,6 @@
   void SizeToFit(int fixed_width);
 
   // View:
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   gfx::Size CalculatePreferredSize() const override;
   int GetHeightForWidth(int w) const override;
   void Layout() override;
diff --git a/ui/views/controls/styled_label_unittest.cc b/ui/views/controls/styled_label_unittest.cc
index fb3831df..dae57fc 100644
--- a/ui/views/controls/styled_label_unittest.cc
+++ b/ui/views/controls/styled_label_unittest.cc
@@ -875,4 +875,36 @@
   EXPECT_EQ(size, styled()->GetPreferredSize());
 }
 
+TEST_F(StyledLabelTest, AccessibleNameAndRole) {
+  const std::string text("Text");
+  InitStyledLabel(text);
+  EXPECT_EQ(styled()->GetAccessibleName(), base::UTF8ToUTF16(text));
+  EXPECT_EQ(styled()->GetAccessibleRole(), ax::mojom::Role::kStaticText);
+
+  ui::AXNodeData data;
+  styled()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.GetStringAttribute(ax::mojom::StringAttribute::kName), text);
+  EXPECT_EQ(data.role, ax::mojom::Role::kStaticText);
+
+  styled()->SetTextContext(style::CONTEXT_DIALOG_TITLE);
+  EXPECT_EQ(styled()->GetAccessibleName(), base::UTF8ToUTF16(text));
+  EXPECT_EQ(styled()->GetAccessibleRole(), ax::mojom::Role::kTitleBar);
+
+  data = ui::AXNodeData();
+  styled()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.GetStringAttribute(ax::mojom::StringAttribute::kName), text);
+  EXPECT_EQ(data.role, ax::mojom::Role::kTitleBar);
+
+  styled()->SetText(u"New Text");
+  styled()->SetAccessibleRole(ax::mojom::Role::kLink);
+  EXPECT_EQ(styled()->GetAccessibleName(), u"New Text");
+  EXPECT_EQ(styled()->GetAccessibleRole(), ax::mojom::Role::kLink);
+
+  data = ui::AXNodeData();
+  styled()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
+            u"New Text");
+  EXPECT_EQ(data.role, ax::mojom::Role::kLink);
+}
+
 }  // namespace views
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index de07dc7f..dc6efb3 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -257,6 +257,8 @@
   AddAccelerator(ui::Accelerator(ui::VKEY_V, ui::EF_CONTROL_DOWN));
 #endif
 
+  SetAccessibilityProperties(ax::mojom::Role::kTextField);
+
   // Sometimes there are additional ignored views, such as the View representing
   // the cursor, inside the text field. These should always be ignored by
   // accessibility since a plain text field should always be a leaf node in the
@@ -1013,10 +1015,7 @@
 }
 
 void Textfield::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  node_data->role = ax::mojom::Role::kTextField;
-
-  node_data->SetName(GetAccessibleName());
-  node_data->SetNameFrom(ax::mojom::NameFrom::kAttribute);
+  View::GetAccessibleNodeData(node_data);
 
   // Editable state indicates support of editable interface, and is always set
   // for a textfield, even if disabled or readonly.
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index 8400210..741df4f 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -4567,6 +4567,22 @@
   EXPECT_TRUE(node_data_protected.HasState(ax::mojom::State::kProtected));
 }
 
+TEST_F(TextfieldTest, AccessibleRole) {
+  InitTextfield();
+
+  ui::AXNodeData data;
+  textfield_->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kTextField);
+  EXPECT_EQ(textfield_->GetAccessibleRole(), ax::mojom::Role::kTextField);
+
+  textfield_->SetAccessibleRole(ax::mojom::Role::kSearchBox);
+
+  data = ui::AXNodeData();
+  textfield_->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kSearchBox);
+  EXPECT_EQ(textfield_->GetAccessibleRole(), ax::mojom::Role::kSearchBox);
+}
+
 // Verify that cursor visibility is controlled by SetCursorEnabled.
 TEST_F(TextfieldTest, CursorVisibility) {
   InitTextfield();
diff --git a/ui/webui/resources/cr_components/history_clusters/page_favicon.html b/ui/webui/resources/cr_components/history_clusters/page_favicon.html
index b9cadcd7..b66015e 100644
--- a/ui/webui/resources/cr_components/history_clusters/page_favicon.html
+++ b/ui/webui/resources/cr_components/history_clusters/page_favicon.html
@@ -22,6 +22,12 @@
     max-height: 100%;
     max-width: 100%;
   }
+
+  :host([is-image-cover_]) #page-image {
+    height: 100%;
+    object-fit: cover;
+    width: 100%;
+  }
 </style>
 
 <template is="dom-if" if="[[imageUrl_]]">
diff --git a/ui/webui/resources/cr_components/history_clusters/page_favicon.ts b/ui/webui/resources/cr_components/history_clusters/page_favicon.ts
index df08b0bb..a10db368d 100644
--- a/ui/webui/resources/cr_components/history_clusters/page_favicon.ts
+++ b/ui/webui/resources/cr_components/history_clusters/page_favicon.ts
@@ -77,6 +77,12 @@
         type: Object,
         value: null,
       },
+
+      isImageCover_: {
+        type: Boolean,
+        value: () => loadTimeData.getBoolean('isHistoryClustersImageCover'),
+        reflectToAttribute: true,
+      },
     };
   }